@bananapus/suckers-v6 0.0.3 → 0.0.5

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # nana-suckers-v5
1
+ # Juicebox Suckers
2
2
 
3
- Cross-chain bridging for Juicebox V5 projects. Suckers let users burn project tokens on one chain and receive the same amount on another, moving the backing funds across via merkle-tree-based claims and chain-specific bridges.
3
+ Cross-chain bridging for Juicebox V6 projects. Suckers let users cash out project tokens on one chain, move the backing funds across a bridge, and mint the same number of project tokens on another chain -- all via merkle-tree-based claims and chain-specific bridges.
4
4
 
5
5
  <details>
6
6
  <summary>Table of Contents</summary>
@@ -9,6 +9,9 @@ Cross-chain bridging for Juicebox V5 projects. Suckers let users burn project to
9
9
  <li><a href="#architecture">Architecture</a></li>
10
10
  <li><a href="#bridging-flow">Bridging Flow</a></li>
11
11
  <li><a href="#bridging-tokens">Bridging Tokens</a></li>
12
+ <li><a href="#token-mapping">Token Mapping</a></li>
13
+ <li><a href="#deprecation-lifecycle">Deprecation Lifecycle</a></li>
14
+ <li><a href="#emergency-hatch">Emergency Hatch</a></li>
12
15
  <li><a href="#launching-suckers">Launching Suckers</a></li>
13
16
  <li><a href="#managing-suckers">Managing Suckers</a></li>
14
17
  <li><a href="#using-the-relayer">Using the Relayer</a></li>
@@ -24,7 +27,7 @@ Cross-chain bridging for Juicebox V5 projects. Suckers let users burn project to
24
27
  </ol>
25
28
  </details>
26
29
 
27
- _If you're having trouble understanding this contract, take a look at the [core protocol contracts](https://github.com/Bananapus/nana-core-v5) and the [documentation](https://docs.juicebox.money/) first. If you have questions, reach out on [Discord](https://discord.com/invite/ErQYmth4dS)._
30
+ _If you're having trouble understanding this contract, take a look at the [core protocol contracts](https://github.com/Bananapus/nana-core-v6) and the [documentation](https://docs.juicebox.money/) first. If you have questions, reach out on [Discord](https://discord.com/invite/ErQYmth4dS)._
28
31
 
29
32
  ## What are Suckers?
30
33
 
@@ -32,17 +35,17 @@ _If you're having trouble understanding this contract, take a look at the [core
32
35
 
33
36
  | Sucker | Networks | Description |
34
37
  |--------|----------|-------------|
35
- | [`JBCCIPSucker`](src/JBCCIPSucker.sol) | Any CCIP-connected chains | Uses [Chainlink CCIP](https://docs.chain.link/ccip) (`ccipSend`/`ccipReceive`). Handles native token wrapping/unwrapping for chains with different native assets. |
38
+ | [`JBCCIPSucker`](src/JBCCIPSucker.sol) | Any CCIP-connected chains | Uses [Chainlink CCIP](https://docs.chain.link/ccip) (`ccipSend`/`ccipReceive`). Handles native token wrapping/unwrapping for chains with different native assets. Supports Ethereum, Optimism, Base, Arbitrum, Polygon, Avalanche, BNB Chain, and their testnets. |
36
39
  | [`JBOptimismSucker`](src/JBOptimismSucker.sol) | Ethereum and Optimism | Uses the [OP Standard Bridge](https://docs.optimism.io/builders/app-developers/bridging/standard-bridge) and the [OP Messenger](https://docs.optimism.io/builders/app-developers/bridging/messaging). |
37
40
  | [`JBBaseSucker`](src/JBBaseSucker.sol) | Ethereum and Base | A thin wrapper around `JBOptimismSucker` with Base chain IDs. |
38
- | [`JBArbitrumSucker`](src/JBArbitrumSucker.sol) | Ethereum and Arbitrum | Uses the [Arbitrum Inbox](https://docs.arbitrum.io/build-decentralized-apps/cross-chain-messaging) and the [Arbitrum Gateway](https://docs.arbitrum.io/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started). Handles L1<->L2 retryable tickets. |
41
+ | [`JBArbitrumSucker`](src/JBArbitrumSucker.sol) | Ethereum and Arbitrum | Uses the [Arbitrum Inbox](https://docs.arbitrum.io/build-decentralized-apps/cross-chain-messaging) and the [Arbitrum Gateway](https://docs.arbitrum.io/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started). Handles L1<->L2 retryable tickets and address aliasing. |
39
42
 
40
43
  Suckers use two [merkle trees](https://en.wikipedia.org/wiki/Merkle_tree) to track project token claims associated with each terminal token they support:
41
44
 
42
- - The **outbox tree** tracks tokens on the local chain the network that the sucker is on.
43
- - The **inbox tree** tracks tokens which have been bridged from the peer chain the network that the sucker's peer is on.
45
+ - The **outbox tree** tracks tokens on the local chain -- the network that the sucker is on.
46
+ - The **inbox tree** tracks tokens which have been bridged from the peer chain -- the network that the sucker's peer is on.
44
47
 
45
- For example, a sucker which supports bridging ETH and USDC would have four trees an inbox and outbox tree for each token. These trees are append-only, and when they're bridged over to the other chain, they aren't deleted they only update the remote inbox tree with the latest root.
48
+ For example, a sucker which supports bridging ETH and USDC would have four trees -- an inbox and outbox tree for each token. These trees are append-only, and when they're bridged over to the other chain, they aren't deleted -- they only update the remote inbox tree with the latest root.
46
49
 
47
50
  To insert project tokens into the outbox tree, users call `JBSucker.prepare(...)` with the amount of project tokens to bridge and the terminal token to bridge with them. The sucker cashes out those project tokens to reclaim the chosen terminal token from the project's primary terminal. Then it inserts a claim with this information into the outbox tree.
48
51
 
@@ -79,21 +82,21 @@ graph TD;
79
82
 
80
83
  | Contract | Description |
81
84
  |----------|-------------|
82
- | `JBSucker` | Abstract base. Manages outbox/inbox merkle trees, `prepare`/`toRemote`/`claim` lifecycle, token mapping, deprecation, and emergency hatch. Deployed as clones via `Initializable`. |
83
- | `JBCCIPSucker` | Extends `JBSucker`. Bridges via Chainlink CCIP (`ccipSend`/`ccipReceive`). Supports any CCIP-connected chain. Handles native token wrapping/unwrapping. |
84
- | `JBOptimismSucker` | Extends `JBSucker`. Bridges via OP Standard Bridge + OP Messenger. |
85
- | `JBBaseSucker` | Thin wrapper around `JBOptimismSucker` with Base chain IDs. |
86
- | `JBArbitrumSucker` | Extends `JBSucker`. Bridges via Arbitrum Inbox + Gateway Router. Handles L1<->L2 retryable tickets. |
87
- | `JBSuckerRegistry` | Tracks all suckers per project. Manages deployer allowlist. Entry point for `deploySuckersFor`. |
88
- | `JBSuckerDeployer` | Abstract base deployer. Clones a singleton sucker via `LibClone.cloneDeterministic` and initializes it. |
89
- | `JBCCIPSuckerDeployer` | Deployer for `JBCCIPSucker`. Stores CCIP router, remote chain ID, and chain selector. |
90
- | `JBOptimismSuckerDeployer` | Deployer for `JBOptimismSucker`. Stores OP Messenger and OP Bridge addresses. |
91
- | `JBBaseSuckerDeployer` | Thin wrapper around `JBOptimismSuckerDeployer` for Base. |
92
- | `JBArbitrumSuckerDeployer` | Deployer for `JBArbitrumSucker`. Stores Arbitrum Inbox, Gateway Router, and layer (L1/L2). |
93
- | `MerkleLib` | Incremental merkle tree (depth 32, modeled on eth2 deposit contract). Used for outbox/inbox trees. |
94
- | `CCIPHelper` | CCIP router addresses, chain selectors, and WETH addresses per chain. |
95
- | `ARBAddresses` | Arbitrum bridge contract addresses (Inbox, Gateway Router) for mainnet and Sepolia. |
96
- | `ARBChains` | Arbitrum chain ID constants. |
85
+ | [`JBSucker`](src/JBSucker.sol) | Abstract base. Manages outbox/inbox merkle trees, `prepare`/`toRemote`/`claim` lifecycle, token mapping, deprecation, and emergency hatch. Deployed as clones via `Initializable`. Uses `ERC2771Context` for meta-transactions. |
86
+ | [`JBCCIPSucker`](src/JBCCIPSucker.sol) | Extends `JBSucker`. Bridges via Chainlink CCIP (`ccipSend`/`ccipReceive`). Supports any CCIP-connected chain pair. Wraps native ETH to WETH before bridging (CCIP only transports ERC-20s) and unwraps on the receiving end. Can map `NATIVE_TOKEN` to ERC-20 addresses on the remote chain (unlike OP/Arbitrum suckers). |
87
+ | [`JBOptimismSucker`](src/JBOptimismSucker.sol) | Extends `JBSucker`. Bridges via OP Standard Bridge + OP Messenger. No `msg.value` required for transport. |
88
+ | [`JBBaseSucker`](src/JBBaseSucker.sol) | Thin wrapper around `JBOptimismSucker` with Base chain IDs (Ethereum 1 <-> Base 8453, Sepolia 11155111 <-> Base Sepolia 84532). |
89
+ | [`JBArbitrumSucker`](src/JBArbitrumSucker.sol) | Extends `JBSucker`. Bridges via Arbitrum Inbox + Gateway Router. Uses `unsafeCreateRetryableTicket` for L1->L2 (to avoid address aliasing of refund address) and `ArbSys.sendTxToL1` for L2->L1. Requires `msg.value` for L1->L2 transport payment. |
90
+ | [`JBSuckerRegistry`](src/JBSuckerRegistry.sol) | Tracks all suckers per project. Manages deployer allowlist (owner-only). Entry point for `deploySuckersFor`. Can remove deprecated suckers via `removeDeprecatedSucker`. |
91
+ | [`JBSuckerDeployer`](src/JBSuckerDeployer.sol) | Abstract base deployer. Clones a singleton sucker via `LibClone.cloneDeterministic` and initializes it. Two-phase setup: `setChainSpecificConstants` then `configureSingleton`. |
92
+ | [`JBCCIPSuckerDeployer`](src/deployers/JBCCIPSuckerDeployer.sol) | Deployer for `JBCCIPSucker`. Stores CCIP router, remote chain ID, and CCIP chain selector. |
93
+ | [`JBOptimismSuckerDeployer`](src/deployers/JBOptimismSuckerDeployer.sol) | Deployer for `JBOptimismSucker`. Stores OP Messenger and OP Bridge addresses. |
94
+ | [`JBBaseSuckerDeployer`](src/deployers/JBBaseSuckerDeployer.sol) | Thin wrapper around `JBOptimismSuckerDeployer` for Base. |
95
+ | [`JBArbitrumSuckerDeployer`](src/deployers/JBArbitrumSuckerDeployer.sol) | Deployer for `JBArbitrumSucker`. Stores Arbitrum Inbox, Gateway Router, and layer (`JBLayer.L1` or `JBLayer.L2`). |
96
+ | [`MerkleLib`](src/utils/MerkleLib.sol) | Incremental merkle tree (depth 32, max ~4 billion leaves, modeled on eth2 deposit contract). Used for outbox/inbox trees. Gas-optimized with inline assembly for `root()` and `branchRoot()`. |
97
+ | [`CCIPHelper`](src/libraries/CCIPHelper.sol) | CCIP router addresses, chain selectors, and WETH addresses per chain. Covers Ethereum, Optimism, Arbitrum, Base, Polygon, Avalanche, and BNB Chain (mainnet and testnets). |
98
+ | [`ARBAddresses`](src/libraries/ARBAddresses.sol) | Arbitrum bridge contract addresses (Inbox, Gateway Router) for mainnet and Sepolia. |
99
+ | [`ARBChains`](src/libraries/ARBChains.sol) | Arbitrum chain ID constants. |
97
100
 
98
101
  ## Bridging Flow
99
102
 
@@ -109,14 +112,19 @@ Chain A Chain B
109
112
  | - sends merkle root + funds -->|
110
113
  | |
111
114
  | 3. fromRemote(root)
112
- | - updates inbox tree
115
+ | - validates message version
116
+ | - updates inbox tree (if nonce > current)
113
117
  | |
114
118
  | 4. claim(proof)
115
119
  | - verifies merkle proof
116
120
  | - mints project tokens
117
- | - adds funds to balance
121
+ | - adds funds to balance (if ON_CLAIM)
118
122
  ```
119
123
 
124
+ Each `toRemote` call increments the outbox nonce and sends the complete merkle root. On the receiving side, `fromRemote` only accepts roots with a nonce strictly greater than the current inbox nonce. If nonces arrive out of order (possible with CCIP), earlier nonces are silently skipped and their claims become unclaimable on that chain. The sender would need to use the emergency hatch on the source chain to recover funds from skipped roots.
125
+
126
+ Messages include a `MESSAGE_VERSION` (currently `1`) to reject incompatible messages from peers running different protocol versions.
127
+
120
128
  ## Bridging Tokens
121
129
 
122
130
  Imagine that "OhioDAO" is deployed on Ethereum mainnet and Optimism:
@@ -153,18 +161,18 @@ JBERC20.approve({
153
161
  });
154
162
  ```
155
163
 
156
- **3. Prepare the bridge.** Jimmy calls `prepare(...)` on the mainnet sucker:
164
+ **3. Prepare the bridge.** Jimmy calls `prepare(...)` on the mainnet sucker. Note that `beneficiary` is `bytes32` for cross-VM compatibility (e.g., Solana public keys):
157
165
 
158
166
  ```solidity
159
167
  JBOptimismSucker.prepare({
160
- projectTokenAmount: 1e18,
161
- beneficiary: jimmy,
168
+ projectTokenCount: 1e18,
169
+ beneficiary: bytes32(uint256(uint160(jimmy))), // bytes32 for cross-VM compat
162
170
  minTokensReclaimed: 0,
163
171
  token: JBConstants.NATIVE_TOKEN
164
172
  });
165
173
  ```
166
174
 
167
- The sucker transfers Jimmy's $OHIO to itself, cashes them out using OhioDAO's primary ETH terminal, and inserts a leaf into the ETH outbox tree. The leaf is a `keccak256` hash of the beneficiary's address, the project token count, and the terminal token amount reclaimed.
175
+ The sucker transfers Jimmy's $OHIO to itself, cashes them out using OhioDAO's primary ETH terminal, and inserts a leaf into the ETH outbox tree. The leaf is a `keccak256` hash of the beneficiary (bytes32), the project token count, and the terminal token amount reclaimed. Both amounts are capped at `uint128` for SVM compatibility.
168
176
 
169
177
  **4. Bridge to remote.** Jimmy (or anyone) calls `toRemote(...)`:
170
178
 
@@ -172,15 +180,15 @@ The sucker transfers Jimmy's $OHIO to itself, cashes them out using OhioDAO's pr
172
180
  JBOptimismSucker.toRemote(JBConstants.NATIVE_TOKEN);
173
181
  ```
174
182
 
175
- This sends the outbox merkle root and the accumulated ETH to the peer sucker on Optimism. After the bridge completes, the Optimism sucker's ETH inbox tree is updated with the new root containing Jimmy's claim.
183
+ This sends the outbox merkle root and the accumulated ETH to the peer sucker on Optimism. The outbox balance is cleared and the nonce incremented. After the bridge completes, the Optimism sucker's ETH inbox tree is updated with the new root containing Jimmy's claim.
176
184
 
177
185
  **5. Claim on the remote chain.** Jimmy claims his $OHIO on Optimism by calling `claim(...)` with a [`JBClaim`](src/structs/JBClaim.sol):
178
186
 
179
187
  ```solidity
180
188
  struct JBClaim {
181
- address token;
182
- JBLeaf leaf;
183
- bytes32[32] proof; // Must be TREE_DEPTH (32) long.
189
+ address token; // The terminal token to claim
190
+ JBLeaf leaf; // The leaf data
191
+ bytes32[32] proof; // Merkle proof (TREE_DEPTH = 32)
184
192
  }
185
193
  ```
186
194
 
@@ -188,14 +196,14 @@ The [`JBLeaf`](src/structs/JBLeaf.sol):
188
196
 
189
197
  ```solidity
190
198
  struct JBLeaf {
191
- uint256 index;
192
- address beneficiary;
193
- uint256 projectTokenCount;
194
- uint256 terminalTokenAmount;
199
+ uint256 index; // Position in the merkle tree
200
+ bytes32 beneficiary; // Recipient address (bytes32 for cross-VM compat)
201
+ uint256 projectTokenCount; // Project tokens to mint
202
+ uint256 terminalTokenAmount; // Terminal tokens reclaimed
195
203
  }
196
204
  ```
197
205
 
198
- Building these claims manually requires tracking every insertion and computing merkle proofs. The [`juicerkle`](https://github.com/Bananapus/juicerkle) service simplifies this `POST` a JSON request to `/claims`:
206
+ Building these claims manually requires tracking every insertion and computing merkle proofs. The [`juicerkle`](https://github.com/Bananapus/juicerkle) service simplifies this -- `POST` a JSON request to `/claims`:
199
207
 
200
208
  ```json
201
209
  {
@@ -217,21 +225,68 @@ The service looks through the entire inbox tree and returns all available claims
217
225
 
218
226
  If the sucker's `ADD_TO_BALANCE_MODE` is `ON_CLAIM`, the bridged ETH is immediately added to OhioDAO's Optimism balance. Otherwise, someone calls `addOutstandingAmountToBalance(token)` to add it manually.
219
227
 
228
+ ## Token Mapping
229
+
230
+ Token mappings define which local terminal token corresponds to which remote terminal token. Key rules:
231
+
232
+ - **`remoteToken` is `bytes32`**, not `address` -- this supports cross-VM compatibility (e.g., Solana program addresses). For EVM addresses, left-pad with zeros: `bytes32(uint256(uint160(address)))`.
233
+ - **Immutable once used.** After an outbox tree has entries for a token, the mapping cannot be changed to a different remote token. It can only be disabled (by setting `remoteToken` to `bytes32(0)`), which triggers a final root flush to settle outstanding claims. A disabled mapping can be re-enabled back to the same remote token.
234
+ - **Minimum gas enforcement.** ERC-20 mappings must specify `minGas >= MESSENGER_ERC20_MIN_GAS_LIMIT` (200,000). Native token mappings on the base `JBSucker` do not require minimum gas, but `JBCCIPSucker` requires it for all tokens (because CCIP wraps native to WETH, an ERC-20 transfer).
235
+ - **Native token rules.** On `JBSucker` (OP/Arb), `NATIVE_TOKEN` can only map to `NATIVE_TOKEN` or `bytes32(0)`. `JBCCIPSucker` overrides this to allow `NATIVE_TOKEN` mapping to any remote address (for chains where ETH is an ERC-20).
236
+ - **`minBridgeAmount`** prevents spam by requiring a minimum outbox balance before `toRemote` can be called.
237
+
238
+ ```solidity
239
+ struct JBTokenMapping {
240
+ address localToken; // Local terminal token address
241
+ uint32 minGas; // Minimum gas for bridging
242
+ bytes32 remoteToken; // Remote token (bytes32 for cross-VM compat)
243
+ uint256 minBridgeAmount; // Minimum balance to trigger bridging
244
+ }
245
+ ```
246
+
247
+ ## Deprecation Lifecycle
248
+
249
+ Suckers have a four-state deprecation lifecycle controlled by `setDeprecation(timestamp)`:
250
+
251
+ ```
252
+ ENABLED --> DEPRECATION_PENDING --> SENDING_DISABLED --> DEPRECATED
253
+ ```
254
+
255
+ | State | Condition | Behavior |
256
+ |-------|-----------|----------|
257
+ | `ENABLED` | `deprecatedAfter == 0` | Fully functional. All operations allowed. |
258
+ | `DEPRECATION_PENDING` | `now < deprecatedAfter - _maxMessagingDelay()` | Warning state. All operations still allowed. Deprecation can be cancelled by setting timestamp to `0`. |
259
+ | `SENDING_DISABLED` | `now < deprecatedAfter` | No new `prepare` or `toRemote` calls. Incoming `fromRemote` still accepted. Emergency exit available for all tokens. |
260
+ | `DEPRECATED` | `now >= deprecatedAfter` | Fully shut down. No new `fromRemote` accepted. Emergency exit available for all tokens. |
261
+
262
+ The deprecation timestamp must be at least `_maxMessagingDelay()` (14 days) in the future. This ensures in-flight messages have time to arrive before the sucker stops accepting them. Once in `SENDING_DISABLED` or `DEPRECATED` state, the deprecation can no longer be modified.
263
+
264
+ Permission: `SET_SUCKER_DEPRECATION` from the project owner.
265
+
266
+ ## Emergency Hatch
267
+
268
+ The emergency hatch lets users exit on the chain where they deposited when the bridge is broken or a token is no longer compatible.
269
+
270
+ - **Per-token activation.** Call `enableEmergencyHatchFor(tokens)` with `SUCKER_SAFETY` permission. This is irreversible -- once opened for a token, that token can never be bridged by this sucker again.
271
+ - **Automatic activation.** When the sucker reaches `SENDING_DISABLED` or `DEPRECATED` state, all tokens automatically allow emergency exit.
272
+ - **Who can exit.** Only users whose leaves have NOT already been sent to the remote chain (i.e., leaf index >= `numberOfClaimsSent`) can use the emergency hatch. This prevents double-spending where the same leaf is claimed on both chains.
273
+ - **How it works.** Users call `exitThroughEmergencyHatch(claimData)` with a proof against the **outbox** tree (not the inbox tree). The sucker mints project tokens and adds terminal tokens to the project balance, just like a normal claim.
274
+
220
275
  ## Launching Suckers
221
276
 
222
277
  Requirements for deploying a sucker pair:
223
278
 
224
279
  1. **Projects on both chains.** Project IDs don't have to match.
225
- 2. **100% cash out rate.** Both projects must have `cashOutTaxRate` of `JBConstants.MAX_CASH_OUT_TAX_RATE` so suckers can fully cash out project tokens for terminal tokens.
280
+ 2. **0% cash out tax rate.** Both projects must have a `cashOutTaxRate` of `0` so suckers can fully cash out project tokens for terminal tokens.
226
281
  3. **Owner minting enabled.** Both projects must have `allowOwnerMinting` set to `true` so suckers can mint bridged project tokens.
227
- 4. **ERC-20 project token.** Both projects must have a deployed ERC-20 token (via `JBController.deployERC20For(...)`).
282
+ 4. **ERC-20 project token.** Both projects must have a deployed ERC-20 token (via `JBController.deployERC20For(...)`). The sucker uses `safeTransferFrom` to pull project tokens from the caller.
228
283
 
229
284
  Suckers are deployed through the [`JBSuckerRegistry`](src/JBSuckerRegistry.sol) on each chain. The registry maps local tokens to remote tokens during deployment, so it needs permission:
230
285
 
231
286
  ```solidity
232
287
  // Give the registry MAP_SUCKER_TOKEN permission for project 12
233
288
  uint256[] memory permissionIds = new uint256[](1);
234
- permissionIds[0] = JBPermissionIds.MAP_SUCKER_TOKEN; // ID 28
289
+ permissionIds[0] = JBPermissionIds.MAP_SUCKER_TOKEN;
235
290
 
236
291
  permissions.setPermissionsFor(
237
292
  projectOwner,
@@ -251,7 +306,7 @@ JBTokenMapping[] memory mappings = new JBTokenMapping[](1);
251
306
  mappings[0] = JBTokenMapping({
252
307
  localToken: JBConstants.NATIVE_TOKEN,
253
308
  minGas: 200_000,
254
- remoteToken: JBConstants.NATIVE_TOKEN,
309
+ remoteToken: bytes32(uint256(uint160(JBConstants.NATIVE_TOKEN))), // bytes32
255
310
  minBridgeAmount: 0.025 ether
256
311
  });
257
312
 
@@ -267,16 +322,17 @@ address[] memory suckers = registry.deploySuckersFor(12, salt, configs);
267
322
  ```
268
323
 
269
324
  - The [`JBTokenMapping`](src/structs/JBTokenMapping.sol) maps local mainnet ETH to remote Optimism ETH.
270
- - `minBridgeAmount` prevents spam ours blocks attempts to bridge less than 0.025 ETH.
271
- - `minGas` requires a gas limit of at least 200,000. If your token has expensive transfer logic, you may need more.
272
- - The [`JBSuckerDeployerConfig`](src/structs/JBSuckerDeployerConfig.sol) specifies which deployer to use. You can only use approved deployers through the registry check for `SuckerDeployerAllowed` events or contact the registry's owner.
325
+ - `remoteToken` is `bytes32`, not `address`. For EVM addresses, use `bytes32(uint256(uint160(addr)))`.
326
+ - `minBridgeAmount` prevents spam -- ours blocks attempts to bridge less than 0.025 ETH.
327
+ - `minGas` requires a gas limit of at least 200,000 for ERC-20s. If your token has expensive transfer logic, you may need more.
328
+ - The [`JBSuckerDeployerConfig`](src/structs/JBSuckerDeployerConfig.sol) specifies which deployer to use. You can only use approved deployers through the registry -- check for `SuckerDeployerAllowed` events or contact the registry's owner.
273
329
  - **For the suckers to be peers, the `salt` has to match on both chains and the same address must call `deploySuckersFor(...)`.**
274
330
 
275
331
  Finally, give the sucker permission to mint bridged project tokens:
276
332
 
277
333
  ```solidity
278
334
  uint256[] memory mintPermissionIds = new uint256[](1);
279
- mintPermissionIds[0] = JBPermissionIds.MINT_TOKENS; // ID 9
335
+ mintPermissionIds[0] = JBPermissionIds.MINT_TOKENS;
280
336
 
281
337
  permissions.setPermissionsFor(
282
338
  projectOwner,
@@ -298,15 +354,15 @@ Once configured, suckers manage themselves. Stay up-to-date on changes to the br
298
354
 
299
355
  ### Disable a token
300
356
 
301
- If a bridge change affects only certain tokens, call `mapToken(...)` with `remoteToken` set to `address(0)` to disable that token. If the bridge won't allow a final transfer with the remaining funds, activate the `EmergencyHatch` for the affected tokens.
357
+ If a bridge change affects only certain tokens, call `mapToken(...)` with `remoteToken` set to `bytes32(0)` to disable that token. This triggers a final `toRemote` to flush remaining outbox funds to the peer. If the bridge won't allow a final transfer with the remaining funds, activate the emergency hatch for the affected tokens instead.
302
358
 
303
- The emergency hatch lets depositors withdraw their funds on the chain where they deposited. Only those whose funds have not been sent to the remote chain can withdraw. Once opened for a token, that token can never be bridged by this sucker again deploy a new sucker instead.
359
+ The emergency hatch lets depositors withdraw their funds on the chain where they deposited. Only those whose funds have not been sent to the remote chain can withdraw. Once opened for a token, that token can never be bridged by this sucker again -- deploy a new sucker instead.
304
360
 
305
361
  ### Deprecate the suckers
306
362
 
307
- If the bridging infrastructure will no longer work, deprecate the sucker to begin shutdown. The deprecation timestamp must be at least `_maxMessagingDelay()` (14 days) in the future to ensure no funds or roots are lost in transit. After this duration, all tokens allow exit through the `EmergencyHatch` and no new messages are accepted. This protects against future fake or malicious bridge messages.
363
+ If the bridging infrastructure will no longer work, deprecate the sucker to begin shutdown. Call `setDeprecation(timestamp)` with a timestamp at least 14 days (`_maxMessagingDelay()`) in the future. The sucker transitions through `DEPRECATION_PENDING` -> `SENDING_DISABLED` -> `DEPRECATED`. After full deprecation, all tokens allow exit through the emergency hatch and no new messages are accepted. This protects against future fake or malicious bridge messages.
308
364
 
309
- When deprecating, ensure no pending bridge messages need retrying once deprecation completes, those messages will be rejected.
365
+ When deprecating, ensure no pending bridge messages need retrying -- once deprecation completes, those messages will be rejected.
310
366
 
311
367
  ## Using the Relayer
312
368
 
@@ -320,14 +376,14 @@ Users can do this manually, but it's a hassle. The [`bananapus-sucker-relayer`](
320
376
 
321
377
  ## Resources
322
378
 
323
- - [`MerkleLib`](src/utils/MerkleLib.sol) Incremental merkle tree based on [Nomad's implementation](https://github.com/nomad-xyz/nomad-monorepo/blob/main/solidity/nomad-core/libs/Merkle.sol) and the eth2 deposit contract.
324
- - [`juicerkle`](https://github.com/Bananapus/juicerkle) Service that returns available claims for a beneficiary (generates merkle proofs). Includes a [Go merkle tree implementation](https://github.com/Bananapus/juicerkle/blob/master/tree/tree.go) for computing roots and building/verifying proofs.
325
- - [`juicerkle-tester`](https://github.com/Bananapus/juicerkle-tester) End-to-end bridging test: deploys projects, tokens, and suckers, then bridges between them. Useful as a bridging walkthrough.
379
+ - [`MerkleLib`](src/utils/MerkleLib.sol) -- Incremental merkle tree based on [Nomad's implementation](https://github.com/nomad-xyz/nomad-monorepo/blob/main/solidity/nomad-core/libs/Merkle.sol) and the eth2 deposit contract.
380
+ - [`juicerkle`](https://github.com/Bananapus/juicerkle) -- Service that returns available claims for a beneficiary (generates merkle proofs). Includes a [Go merkle tree implementation](https://github.com/Bananapus/juicerkle/blob/master/tree/tree.go) for computing roots and building/verifying proofs.
381
+ - [`juicerkle-tester`](https://github.com/Bananapus/juicerkle-tester) -- End-to-end bridging test: deploys projects, tokens, and suckers, then bridges between them. Useful as a bridging walkthrough.
326
382
 
327
383
  ## Repository Layout
328
384
 
329
385
  ```
330
- nana-suckers-v5/
386
+ nana-suckers-v6/
331
387
  ├── script/
332
388
  │ ├── Deploy.s.sol - Deployment script.
333
389
  │ └── helpers/
@@ -348,6 +404,7 @@ nana-suckers-v5/
348
404
  │ └── MerkleLib.sol - Incremental merkle tree (depth 32).
349
405
  └── test/
350
406
  ├── Fork.t.sol - Fork tests.
407
+ ├── InteropCompat.t.sol - Cross-VM compatibility tests.
351
408
  ├── SuckerAttacks.t.sol - Security-focused attack tests.
352
409
  ├── SuckerDeepAttacks.t.sol - Deep attack scenario tests.
353
410
  ├── mocks/ - Mock contracts for testing.
@@ -361,20 +418,20 @@ nana-suckers-v5/
361
418
  For projects using `npm` to manage dependencies (recommended):
362
419
 
363
420
  ```bash
364
- npm install @bananapus/suckers-v5
421
+ npm install @bananapus/suckers-v6
365
422
  ```
366
423
 
367
424
  For projects using `forge` to manage dependencies:
368
425
 
369
426
  ```bash
370
- forge install Bananapus/nana-suckers-v5
427
+ forge install Bananapus/nana-suckers-v6
371
428
  ```
372
429
 
373
- If you're using `forge`, add `@bananapus/suckers-v5/=lib/nana-suckers-v5/` to `remappings.txt`. You'll also need to install `nana-suckers-v5`'s dependencies and add similar remappings for them.
430
+ If you're using `forge`, add `@bananapus/suckers-v6/=lib/nana-suckers-v6/` to `remappings.txt`. You'll also need to install `nana-suckers-v6`'s dependencies and add similar remappings for them.
374
431
 
375
432
  ### Develop
376
433
 
377
- `nana-suckers-v5` uses [npm](https://www.npmjs.com/) (version >=20.0.0) for package management and the [Foundry](https://github.com/foundry-rs/foundry) development toolchain for builds, tests, and deployments. To get set up, [install Node.js](https://nodejs.org/en/download) and install [Foundry](https://github.com/foundry-rs/foundry):
434
+ `nana-suckers-v6` uses [npm](https://www.npmjs.com/) (version >=20.0.0) for package management and the [Foundry](https://github.com/foundry-rs/foundry) development toolchain for builds, tests, and deployments. To get set up, [install Node.js](https://nodejs.org/en/download) and install [Foundry](https://github.com/foundry-rs/foundry):
378
435
 
379
436
  ```bash
380
437
  curl -L https://foundry.paradigm.xyz | sh