@bananapus/suckers-v6 0.0.69 → 0.0.71

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
@@ -64,6 +64,7 @@ That means every bridge path has two trust surfaces:
64
64
  - do not reason about suckers as if they were generic ERC-20 bridges
65
65
  - root ordering and message delivery semantics matter as much as proof format
66
66
  - token mapping is part of the economic invariant
67
+ - peer contexts are merged only when they share both currency and decimals; same-currency contexts with different decimals are kept separate and valued independently at read time, never summed across precisions
67
68
  - emergency and deprecation paths are part of normal operational safety
68
69
 
69
70
  ## Where State Lives
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/suckers-v6",
3
- "version": "0.0.69",
3
+ "version": "0.0.71",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@arbitrum/nitro-contracts": "3.2.0",
29
- "@bananapus/core-v6": "^0.0.78",
29
+ "@bananapus/core-v6": "^0.0.81",
30
30
  "@bananapus/permission-ids-v6": "^0.0.28",
31
31
  "@chainlink/contracts-ccip": "1.6.4",
32
32
  "@chainlink/local": "0.2.7",
package/src/JBSucker.sol CHANGED
@@ -545,12 +545,18 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
545
545
  _cachedCurrencyOf[contextToken] = contextCurrency;
546
546
  }
547
547
 
548
- // Accumulate into an existing same-currency entry, or append a new one. The context set is small
549
- // (one entry per distinct local currency), so a linear scan is cheaper than a mapping.
548
+ // Accumulate into an existing entry that matches on BOTH currency AND decimals, or append a new one.
549
+ // The decimals must match: `surplus`/`balance` are raw, un-valued token amounts, so two contexts that
550
+ // share a currency but carry different decimals (e.g. a 6-decimal and an 18-decimal representation of
551
+ // the same currency) are on different scales and CANNOT be summed directly — doing so would corrupt
552
+ // the
553
+ // aggregate. Keeping them as separate entries lets the read side (`remoteSurplusOf` -> `_valued`)
554
+ // decimal-adjust each one independently before summing. The context set is small (one entry per
555
+ // distinct local currency+decimals), so a linear scan is cheaper than a mapping.
550
556
  uint256 numStored = _peerContexts.length;
551
557
  bool merged;
552
558
  for (uint256 j; j < numStored;) {
553
- if (_peerContexts[j].currency == contextCurrency) {
559
+ if (_peerContexts[j].currency == contextCurrency && _peerContexts[j].decimals == ctx.decimals) {
554
560
  _peerContexts[j].surplus = _saturatingAddU128(_peerContexts[j].surplus, ctx.surplus);
555
561
  _peerContexts[j].balance = _saturatingAddU128(_peerContexts[j].balance, ctx.balance);
556
562
  merged = true;
@@ -1293,9 +1299,8 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
1293
1299
  // Record the balance before the cash out for the sanity check.
1294
1300
  uint256 balanceBefore = _balanceOf({token: token, addr: address(this)});
1295
1301
 
1296
- // Cash out the project tokens for terminal tokens. Suckers are a transparent value-mover (the bridge
1297
- // accounting is the entirety of their function) — they're not a fee-paying entry point for any referrer,
1298
- // so `referralProjectId: 0` is correct.
1302
+ // Cash out the project tokens for terminal tokens. Suckers are a transparent value-mover; the bridge
1303
+ // accounting is the entirety of their function.
1299
1304
  reclaimedAmount = terminal.cashOutTokensOf({
1300
1305
  holder: address(this),
1301
1306
  projectId: cachedProjectId,
@@ -1303,8 +1308,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
1303
1308
  tokenToReclaim: token,
1304
1309
  minTokensReclaimed: minTokensReclaimed,
1305
1310
  beneficiary: payable(address(this)),
1306
- metadata: bytes(""),
1307
- referralProjectId: 0
1311
+ metadata: bytes("")
1308
1312
  });
1309
1313
 
1310
1314
  // Sanity check to make sure we received the expected amount.
@@ -6,11 +6,9 @@ pragma solidity ^0.8.0;
6
6
  /// @custom:member beneficiary The beneficiary of the leaf.
7
7
  /// @custom:member projectTokenCount The number of project tokens to claim.
8
8
  /// @custom:member terminalTokenAmount The amount of terminal tokens to claim.
9
- /// @custom:member metadata Opaque, caller-defined attribution payload that travels with the leaf inside the merkle
10
- /// root. Use cases include cross-chain referral split hooks tagging a leaf with `(originChainId, referralProjectId)`
11
- /// so the destination contract can settle the bridged value atomically. The sucker protocol itself never inspects
12
- /// this field — it's covered by the leaf hash, so receivers can trust the value once the merkle proof verifies.
13
- /// Pass `bytes32(0)` when no attribution context is needed.
9
+ /// @custom:member metadata Opaque, caller-defined payload that travels with the leaf inside the merkle root. The
10
+ /// sucker protocol itself never inspects this field it's covered by the leaf hash, so receivers can trust the value
11
+ /// once the merkle proof verifies. Pass `bytes32(0)` when no extra claim context is needed.
14
12
  struct JBLeaf {
15
13
  uint256 index;
16
14
  bytes32 beneficiary;