@bananapus/suckers-v6 0.0.70 → 0.0.72
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 +10 -10
- package/package.json +2 -2
- package/references/operations.md +5 -5
- package/references/runtime.md +5 -5
- package/src/JBArbitrumSucker.sol +2 -0
- package/src/JBCCIPSucker.sol +11 -0
- package/src/JBOptimismSucker.sol +1 -0
- package/src/JBSucker.sol +82 -25
- package/src/JBSuckerRegistry.sol +14 -4
- package/src/archive/JBSwapCCIPSucker.sol +21 -0
- package/src/archive/JBSwapCCIPSuckerDeployer.sol +3 -0
- package/src/archive/JBSwapPoolLib.sol +13 -0
- package/src/archive/README.md +5 -0
- package/src/deployers/JBSuckerDeployer.sol +15 -1
- package/src/interfaces/IJBSucker.sol +2 -0
- package/src/libraries/CCIPHelper.sol +1 -0
- package/src/structs/JBLeaf.sol +3 -5
- package/src/utils/MerkleLib.sol +3 -1
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ Use this repo when the requirement is canonical project-token movement across ch
|
|
|
31
31
|
|
|
32
32
|
The main idea is not "bridge the token contract." The main idea is "bridge a Juicebox claim plus enough information to recreate the project-token position on the remote chain."
|
|
33
33
|
|
|
34
|
-
## Key
|
|
34
|
+
## Key contracts
|
|
35
35
|
|
|
36
36
|
| Contract | Role |
|
|
37
37
|
| --- | --- |
|
|
@@ -39,7 +39,7 @@ The main idea is not "bridge the token contract." The main idea is "bridge a Jui
|
|
|
39
39
|
| `JBSuckerRegistry` | Registry for per-project sucker deployments, deployer allowlists, and shared bridge fee settings. |
|
|
40
40
|
| Chain-specific suckers | Transport-specific implementations for OP Stack, Arbitrum, CCIP, and related environments. |
|
|
41
41
|
|
|
42
|
-
## Mental
|
|
42
|
+
## Mental model
|
|
43
43
|
|
|
44
44
|
Each sucker pair has two jobs:
|
|
45
45
|
|
|
@@ -51,7 +51,7 @@ That means every bridge path has two trust surfaces:
|
|
|
51
51
|
- the shared sucker accounting and Merkle logic
|
|
52
52
|
- the bridge-specific transport implementation
|
|
53
53
|
|
|
54
|
-
## Read
|
|
54
|
+
## Read these files first
|
|
55
55
|
|
|
56
56
|
1. `src/JBSucker.sol`
|
|
57
57
|
2. `src/JBSuckerRegistry.sol`
|
|
@@ -59,7 +59,7 @@ That means every bridge path has two trust surfaces:
|
|
|
59
59
|
4. the matching deployer under `src/deployers/`
|
|
60
60
|
5. `src/utils/MerkleLib.sol`
|
|
61
61
|
|
|
62
|
-
## Integration
|
|
62
|
+
## Integration traps
|
|
63
63
|
|
|
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
|
|
@@ -67,13 +67,13 @@ That means every bridge path has two trust surfaces:
|
|
|
67
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
|
|
68
68
|
- emergency and deprecation paths are part of normal operational safety
|
|
69
69
|
|
|
70
|
-
## Where
|
|
70
|
+
## Where state lives
|
|
71
71
|
|
|
72
72
|
- per-claim and tree progression state: the sucker pair
|
|
73
73
|
- deployment inventory and shared operational config: `JBSuckerRegistry`
|
|
74
74
|
- bridge transport assumptions: the chain-specific implementation and its external counterparties
|
|
75
75
|
|
|
76
|
-
## High-
|
|
76
|
+
## High-signal tests
|
|
77
77
|
|
|
78
78
|
1. `test/unit/registry.t.sol`
|
|
79
79
|
2. `test/unit/multi_chain_evolution.t.sol`
|
|
@@ -101,11 +101,11 @@ Useful scripts:
|
|
|
101
101
|
- `npm run deploy:testnets`
|
|
102
102
|
- `npm run analyze`
|
|
103
103
|
|
|
104
|
-
## Deployment
|
|
104
|
+
## Deployment notes
|
|
105
105
|
|
|
106
106
|
This package supports multiple bridge families and is intentionally split into bridge-specific deployers. Operational support is narrower than "all theoretically bridgeable chains" and should be taken from the configured deployers, helper libraries, and deployment scripts in this repo.
|
|
107
107
|
|
|
108
|
-
## Repository
|
|
108
|
+
## Repository layout
|
|
109
109
|
|
|
110
110
|
```text
|
|
111
111
|
src/
|
|
@@ -125,14 +125,14 @@ script/
|
|
|
125
125
|
helpers/
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
## Risks
|
|
128
|
+
## Risks and notes
|
|
129
129
|
|
|
130
130
|
- out-of-order root delivery can make some claims unavailable until an operator uses an emergency path
|
|
131
131
|
- bridge-specific transport assumptions matter as much as the shared sucker logic
|
|
132
132
|
- token mapping and deprecation controls are governance-sensitive surfaces
|
|
133
133
|
- a bridge that stays live operationally still may not be economically safe for every asset or chain pair
|
|
134
134
|
|
|
135
|
-
## For AI
|
|
135
|
+
## For AI agents
|
|
136
136
|
|
|
137
137
|
- Do not summarize this repo as a generic token bridge.
|
|
138
138
|
- Always separate shared sucker logic from bridge-specific transport behavior.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananapus/suckers-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.72",
|
|
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.
|
|
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/references/operations.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
# Suckers
|
|
1
|
+
# Suckers operations
|
|
2
2
|
|
|
3
|
-
## Configuration
|
|
3
|
+
## Configuration surface
|
|
4
4
|
|
|
5
5
|
- [`src/JBSuckerRegistry.sol`](../src/JBSuckerRegistry.sol) is the first stop for deployer allowlists, shared fees, project inventory, and deprecation helpers.
|
|
6
6
|
- Transport-specific deployers in `src/deployers/` are where chain-specific constants and bridge addresses live.
|
|
7
7
|
- [`script/Deploy.s.sol`](../script/Deploy.s.sol) is where deployment-time environment wiring belongs.
|
|
8
8
|
|
|
9
|
-
## Change
|
|
9
|
+
## Change checklist
|
|
10
10
|
|
|
11
11
|
- If you edit base sucker accounting, verify claim flow across at least one chain-specific implementation.
|
|
12
12
|
- If you edit token mapping logic, re-check the registry and deployer assumptions that feed it.
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
- If you edit snapshot or claim-boundary logic, verify `numberOfClaimsSent`, peer snapshots, and emergency exit behavior together.
|
|
16
16
|
- If you touch bridge-specific code, confirm whether the real bug is transport-side or shared accounting-side.
|
|
17
17
|
|
|
18
|
-
## Common
|
|
18
|
+
## Common failure modes
|
|
19
19
|
|
|
20
20
|
- Cross-chain issue is blamed on transport when the root or token mapping was wrong before message delivery.
|
|
21
21
|
- Registry configuration drifts from what a deployer or external operator expects.
|
|
22
22
|
- Emergency hatches or deprecation paths are stale because nobody exercises them until stress conditions arrive.
|
|
23
23
|
|
|
24
|
-
## Useful
|
|
24
|
+
## Useful proof points
|
|
25
25
|
|
|
26
26
|
- [`test/SuckerAttacks.t.sol`](../test/SuckerAttacks.t.sol), [`test/SuckerDeepAttacks.t.sol`](../test/SuckerDeepAttacks.t.sol), and [`test/TestRegressionGaps.sol`](../test/TestRegressionGaps.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.
|
package/references/runtime.md
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
# Suckers
|
|
1
|
+
# Suckers runtime
|
|
2
2
|
|
|
3
|
-
## Core
|
|
3
|
+
## Core roles
|
|
4
4
|
|
|
5
5
|
- [`src/JBSucker.sol`](../src/JBSucker.sol) owns the shared prepare, relay, claim, token-mapping, and lifecycle logic.
|
|
6
6
|
- [`src/JBSuckerRegistry.sol`](../src/JBSuckerRegistry.sol) owns project-to-sucker inventory, deployer allowlists, and shared remote-fee settings.
|
|
7
7
|
- Chain-specific sucker contracts such as [`src/JBArbitrumSucker.sol`](../src/JBArbitrumSucker.sol), [`src/JBOptimismSucker.sol`](../src/JBOptimismSucker.sol), [`src/JBCCIPSucker.sol`](../src/JBCCIPSucker.sol), and [`src/JBCeloSucker.sol`](../src/JBCeloSucker.sol) own transport-specific delivery and verification.
|
|
8
8
|
- Matching deployers under `src/deployers/` own clone and transport configuration.
|
|
9
9
|
|
|
10
|
-
## Runtime
|
|
10
|
+
## Runtime path
|
|
11
11
|
|
|
12
12
|
1. Local state is prepared into a claimable Merkle leaf.
|
|
13
13
|
2. A root is relayed to the peer chain through the bridge-specific transport.
|
|
14
14
|
3. The remote side records the root in its inbox state.
|
|
15
15
|
4. Claimants prove inclusion and recreate their position on the destination chain.
|
|
16
16
|
|
|
17
|
-
## High-
|
|
17
|
+
## High-risk areas
|
|
18
18
|
|
|
19
19
|
- Token mapping: mapping mistakes break economic equivalence, not just UX.
|
|
20
20
|
- Root ordering and replay protection: message sequencing is part of correctness.
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
- Shared accounting vs transport logic: many incidents stem from confusing these layers.
|
|
23
23
|
- Peer snapshots and `numberOfClaimsSent`: these guard against double-spend at the cost of conservative locking when timing goes wrong.
|
|
24
24
|
|
|
25
|
-
## Tests
|
|
25
|
+
## Tests to trust first
|
|
26
26
|
|
|
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.
|
package/src/JBArbitrumSucker.sol
CHANGED
|
@@ -34,6 +34,8 @@ contract JBArbitrumSucker is JBSucker, IJBArbitrumSucker {
|
|
|
34
34
|
// --------------------------- custom errors ------------------------- //
|
|
35
35
|
//*********************************************************************//
|
|
36
36
|
|
|
37
|
+
/// @notice Thrown when the provided transport payment is insufficient to cover the cost of the Arbitrum retryable
|
|
38
|
+
/// ticket.
|
|
37
39
|
error JBArbitrumSucker_NotEnoughGas(uint256 payment, uint256 cost);
|
|
38
40
|
|
|
39
41
|
//*********************************************************************//
|
package/src/JBCCIPSucker.sol
CHANGED
|
@@ -36,11 +36,22 @@ contract JBCCIPSucker is JBSucker, IAny2EVMMessageReceiver {
|
|
|
36
36
|
// --------------------------- custom errors ------------------------- //
|
|
37
37
|
//*********************************************************************//
|
|
38
38
|
|
|
39
|
+
/// @notice Thrown when the configured CCIP router address is the zero address.
|
|
39
40
|
error JBCCIPSucker_InvalidRouter(address router);
|
|
41
|
+
|
|
42
|
+
/// @notice Thrown when an incoming root message claims a positive amount but no tokens were delivered with it.
|
|
40
43
|
error JBCCIPSucker_PositiveRootWithoutDelivery(uint256 rootAmount);
|
|
44
|
+
|
|
45
|
+
/// @notice Thrown when the amount of tokens delivered is less than the amount declared in the root message.
|
|
41
46
|
error JBCCIPSucker_UnderDeliveredAmount(uint256 delivered, uint256 rootAmount);
|
|
47
|
+
|
|
48
|
+
/// @notice Thrown when an incoming message delivers an unexpected number of token transfers.
|
|
42
49
|
error JBCCIPSucker_UnexpectedDeliveredTokens(uint256 count);
|
|
50
|
+
|
|
51
|
+
/// @notice Thrown when an incoming message has an unrecognized message type prefix.
|
|
43
52
|
error JBCCIPSucker_UnknownMessageType(uint8 messageType);
|
|
53
|
+
|
|
54
|
+
/// @notice Thrown when the token delivered with an incoming message does not match the expected token.
|
|
44
55
|
error JBCCIPSucker_WrongDeliveredToken(address delivered, address expected);
|
|
45
56
|
|
|
46
57
|
//*********************************************************************//
|
package/src/JBOptimismSucker.sol
CHANGED
|
@@ -76,6 +76,7 @@ contract JBOptimismSucker is JBSucker, IJBOptimismSucker {
|
|
|
76
76
|
|
|
77
77
|
/// @notice Checks if the `sender` (`_msgSender()`) is a valid representative of the remote peer.
|
|
78
78
|
/// @param sender The message's sender.
|
|
79
|
+
/// @return valid A flag if the sender is a valid representative of the remote peer.
|
|
79
80
|
function _isRemotePeer(address sender) internal override returns (bool valid) {
|
|
80
81
|
return sender == address(OPMESSENGER) && _toBytes32(OPMESSENGER.xDomainMessageSender()) == peer();
|
|
81
82
|
}
|
package/src/JBSucker.sol
CHANGED
|
@@ -68,31 +68,88 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
68
68
|
// --------------------------- custom errors ------------------------- //
|
|
69
69
|
//*********************************************************************//
|
|
70
70
|
|
|
71
|
+
/// @notice Thrown when a terminal-token or project-token amount being bridged exceeds the `uint128` cap enforced
|
|
72
|
+
/// for cross-VM compatibility.
|
|
71
73
|
error JBSucker_AmountExceedsUint128(uint256 amount);
|
|
74
|
+
|
|
75
|
+
/// @notice Thrown when a token mapping specifies a bridging gas limit below the minimum required to safely deliver
|
|
76
|
+
/// an ERC-20 on the remote chain.
|
|
72
77
|
error JBSucker_BelowMinGas(uint256 minGas, uint256 minGasLimit);
|
|
78
|
+
|
|
79
|
+
/// @notice Thrown when an outbound action is attempted while the sucker is deprecated (or no longer accepting
|
|
80
|
+
/// sends).
|
|
73
81
|
error JBSucker_Deprecated(JBSuckerState state);
|
|
82
|
+
|
|
83
|
+
/// @notice Thrown when a deprecation is scheduled for a time sooner than the minimum allowed delay.
|
|
74
84
|
error JBSucker_DeprecationTimestampTooSoon(uint256 givenTime, uint256 minimumTime);
|
|
85
|
+
|
|
86
|
+
/// @notice Thrown when a native token transport payment is required but no `msg.value` was sent.
|
|
75
87
|
error JBSucker_ExpectedMsgValue(uint256 msgValue);
|
|
88
|
+
|
|
89
|
+
/// @notice Thrown when a merkle leaf index is greater than or equal to the maximum number of leaves the tree can
|
|
90
|
+
/// hold.
|
|
76
91
|
error JBSucker_IndexOutOfRange(uint256 index);
|
|
92
|
+
|
|
93
|
+
/// @notice Thrown when the amount to add to the project's balance exceeds the funds available to the sucker.
|
|
77
94
|
error JBSucker_InsufficientBalance(uint256 amount, uint256 balance);
|
|
95
|
+
|
|
96
|
+
/// @notice Thrown when the `msg.value` sent is less than the required `toRemoteFee`.
|
|
78
97
|
error JBSucker_InsufficientMsgValue(uint256 received, uint256 expected);
|
|
98
|
+
|
|
99
|
+
/// @notice Thrown when an incoming bridge message has a format version that does not match the expected version.
|
|
79
100
|
error JBSucker_InvalidMessageVersion(uint8 received, uint8 expected);
|
|
101
|
+
|
|
102
|
+
/// @notice Thrown when the native token is mapped to a remote token that is neither the native token nor the zero
|
|
103
|
+
/// address.
|
|
80
104
|
error JBSucker_InvalidNativeRemoteAddress(bytes32 remoteToken);
|
|
105
|
+
|
|
106
|
+
/// @notice Thrown when a claim's merkle proof does not validate against the stored inbox root.
|
|
81
107
|
error JBSucker_InvalidProof(bytes32 root, bytes32 inboxRoot);
|
|
108
|
+
|
|
109
|
+
/// @notice Thrown when a leaf at the given index has already been executed for the given token.
|
|
82
110
|
error JBSucker_LeafAlreadyExecuted(address token, uint256 index);
|
|
111
|
+
|
|
112
|
+
/// @notice Thrown when no terminal can be found for the given project and token.
|
|
83
113
|
error JBSucker_NoTerminalForToken(uint256 projectId, address token);
|
|
114
|
+
|
|
115
|
+
/// @notice Thrown when the caller is not a valid representative of the remote peer sucker.
|
|
84
116
|
error JBSucker_NotPeer(bytes32 caller);
|
|
117
|
+
|
|
118
|
+
/// @notice Thrown when a send is attempted but there is nothing new in the outbox to bridge.
|
|
85
119
|
error JBSucker_NothingToSend(address token, uint256 outboxBalance, uint256 treeCount, uint256 numberOfClaimsSent);
|
|
120
|
+
|
|
121
|
+
/// @notice Thrown when an account attempts to claim a retained failed-fee refund but is owed nothing.
|
|
86
122
|
error JBSucker_NoRetainedToRemoteFee(address account);
|
|
123
|
+
|
|
124
|
+
/// @notice Thrown when an account attempts to claim a retained failed transport-payment refund but is owed nothing.
|
|
87
125
|
error JBSucker_NoRetainedTransportPaymentRefund(address account);
|
|
126
|
+
|
|
127
|
+
/// @notice Thrown when a native token refund transfer to the beneficiary fails.
|
|
88
128
|
error JBSucker_RefundFailed(address beneficiary, uint256 amount);
|
|
129
|
+
|
|
130
|
+
/// @notice Thrown when a token mapping targets a remote token that another local token has already reserved.
|
|
89
131
|
error JBSucker_RemoteTokenAlreadyMapped(bytes32 remoteToken, address localToken);
|
|
132
|
+
|
|
133
|
+
/// @notice Thrown when remapping a local token whose outbox tree already has entries, which is no longer permitted.
|
|
90
134
|
error JBSucker_TokenAlreadyMapped(address localToken, bytes32 mappedTo);
|
|
135
|
+
|
|
136
|
+
/// @notice Thrown when an emergency-hatch action is attempted for a token whose emergency hatch state does not
|
|
137
|
+
/// allow it.
|
|
91
138
|
error JBSucker_TokenHasInvalidEmergencyHatchState(address token);
|
|
139
|
+
|
|
140
|
+
/// @notice Thrown when an action references a token that has not been mapped to a remote token.
|
|
92
141
|
error JBSucker_TokenNotMapped(address token);
|
|
142
|
+
|
|
143
|
+
/// @notice Thrown when `msg.value` is sent for an action that expects none.
|
|
93
144
|
error JBSucker_UnexpectedMsgValue(uint256 value);
|
|
145
|
+
|
|
146
|
+
/// @notice Thrown when a required beneficiary address is the zero address.
|
|
94
147
|
error JBSucker_ZeroBeneficiary(bytes32 beneficiary);
|
|
148
|
+
|
|
149
|
+
/// @notice Thrown when bridging is attempted for a project that has no ERC-20 token deployed.
|
|
95
150
|
error JBSucker_ZeroERC20Token(uint256 projectId);
|
|
151
|
+
|
|
152
|
+
/// @notice Thrown when a bridge is queued with zero project tokens.
|
|
96
153
|
error JBSucker_ZeroProjectTokenCount();
|
|
97
154
|
|
|
98
155
|
//*********************************************************************//
|
|
@@ -171,14 +228,16 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
171
228
|
uint256 public retainedTransportPaymentRefundBalance;
|
|
172
229
|
|
|
173
230
|
/// @notice The retained failed-fee ETH owed to each original `toRemote` caller.
|
|
231
|
+
/// @custom:param account The address owed the retained ETH.
|
|
174
232
|
mapping(address account => uint256 amount) public retainedToRemoteFeeOf;
|
|
175
233
|
|
|
176
234
|
/// @notice The retained failed transport-payment refund ETH owed to each original bridge caller.
|
|
235
|
+
/// @custom:param account The address owed the retained ETH.
|
|
177
236
|
mapping(address account => uint256 amount) public retainedTransportPaymentRefundOf;
|
|
178
237
|
|
|
179
238
|
/// @notice The source chain freshness key for the most recent accepted peer snapshot.
|
|
180
239
|
/// @dev Only snapshots with a strictly newer source freshness key are accepted, preventing stale rollbacks.
|
|
181
|
-
///
|
|
240
|
+
/// Named to align with the `JBMessageRoot.sourceTimestamp` field it tracks.
|
|
182
241
|
/// Returns 0 if no snapshot has been received yet.
|
|
183
242
|
uint256 public snapshotTimestamp;
|
|
184
243
|
|
|
@@ -401,7 +460,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
401
460
|
}
|
|
402
461
|
|
|
403
462
|
/// @notice Emergency escape hatch: lets a user reclaim their project tokens and terminal tokens on the chain they
|
|
404
|
-
///
|
|
463
|
+
/// deposited from, when the bridge has become permanently non-functional. Must be enabled by the project
|
|
405
464
|
/// owner via `enableEmergencyHatchFor`.
|
|
406
465
|
/// @param claimData The terminal token, merkle tree leaf, and proof for the claim.
|
|
407
466
|
function exitThroughEmergencyHatch(JBClaim calldata claimData) external override {
|
|
@@ -683,7 +742,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
683
742
|
revert JBSucker_TokenNotMapped({token: token});
|
|
684
743
|
}
|
|
685
744
|
|
|
686
|
-
// Make sure that the sucker still allows sending new
|
|
745
|
+
// Make sure that the sucker still allows sending new messages.
|
|
687
746
|
_requireSendingEnabled();
|
|
688
747
|
|
|
689
748
|
// Transfer the tokens to this contract.
|
|
@@ -711,7 +770,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
711
770
|
/// 14-day buffer ensures in-flight messages have time to arrive before the sucker fully shuts down.
|
|
712
771
|
/// @param timestamp The time after which the sucker will be deprecated. Or `0` to remove the upcoming deprecation.
|
|
713
772
|
function setDeprecation(uint40 timestamp) external override {
|
|
714
|
-
// As long as the sucker has not started letting users
|
|
773
|
+
// As long as the sucker has not started letting users withdraw, its deprecation time can be
|
|
715
774
|
// extended/shortened.
|
|
716
775
|
_requireSendingEnabled();
|
|
717
776
|
|
|
@@ -930,14 +989,14 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
930
989
|
return JBSuckerState.DEPRECATION_PENDING;
|
|
931
990
|
}
|
|
932
991
|
|
|
933
|
-
// The sucker
|
|
934
|
-
// Additionally it
|
|
992
|
+
// The sucker no longer sends new roots to the pair, but it accepts new incoming roots.
|
|
993
|
+
// Additionally it lets users exit here, since the sucker can no longer send roots/tokens.
|
|
935
994
|
// forge-lint: disable-next-line(block-timestamp)
|
|
936
995
|
if (block.timestamp < _deprecatedAfter) {
|
|
937
996
|
return JBSuckerState.SENDING_DISABLED;
|
|
938
997
|
}
|
|
939
998
|
|
|
940
|
-
// The sucker is
|
|
999
|
+
// The sucker is in the final state of deprecation. It does not allow new roots.
|
|
941
1000
|
return JBSuckerState.DEPRECATED;
|
|
942
1001
|
}
|
|
943
1002
|
|
|
@@ -1027,7 +1086,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1027
1086
|
// --------------------- internal transactions ----------------------- //
|
|
1028
1087
|
//*********************************************************************//
|
|
1029
1088
|
|
|
1030
|
-
/// @notice Adds funds to the
|
|
1089
|
+
/// @notice Adds funds to the project's balance.
|
|
1031
1090
|
/// @param token The terminal token to add to the project's balance.
|
|
1032
1091
|
/// @param amount The amount of terminal tokens to add to the project's balance.
|
|
1033
1092
|
/// @param cachedProjectId The cached project ID to avoid redundant storage reads.
|
|
@@ -1125,7 +1184,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1125
1184
|
)
|
|
1126
1185
|
internal
|
|
1127
1186
|
{
|
|
1128
|
-
// Guard against amounts that would overflow uint128 on SVM
|
|
1187
|
+
// Guard against amounts that would overflow uint128 on SVM, which caps bridged amounts at uint128.
|
|
1129
1188
|
if (terminalTokenAmount > type(uint128).max) revert JBSucker_AmountExceedsUint128(terminalTokenAmount);
|
|
1130
1189
|
if (projectTokenCount > type(uint128).max) revert JBSucker_AmountExceedsUint128(projectTokenCount);
|
|
1131
1190
|
// Build a hash based on the token amounts, the beneficiary, and the attribution metadata.
|
|
@@ -1167,7 +1226,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1167
1226
|
/// different remote token -- it can only be disabled by mapping to `address(0)`, which triggers a final root
|
|
1168
1227
|
/// flush to settle outstanding claims. This permanence prevents double-spending: if a remapping were allowed
|
|
1169
1228
|
/// after outbox activity, the same local funds could be claimed against two different remote tokens. A
|
|
1170
|
-
/// misconfigured mapping therefore requires deploying a new sucker. Re-enabling a
|
|
1229
|
+
/// misconfigured mapping therefore requires deploying a new sucker. Re-enabling a disabled mapping
|
|
1171
1230
|
/// (back to the same remote token) is supported.
|
|
1172
1231
|
/// @dev Remote tokens are also unique per local token within this sucker. The source side keeps separate
|
|
1173
1232
|
/// outboxes/nonces per local token, but the destination side stores roots under the remote token address. Sharing
|
|
@@ -1299,9 +1358,8 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1299
1358
|
// Record the balance before the cash out for the sanity check.
|
|
1300
1359
|
uint256 balanceBefore = _balanceOf({token: token, addr: address(this)});
|
|
1301
1360
|
|
|
1302
|
-
// Cash out the project tokens for terminal tokens. Suckers are a transparent value-mover
|
|
1303
|
-
// accounting is the entirety of their function
|
|
1304
|
-
// so `referralProjectId: 0` is correct.
|
|
1361
|
+
// Cash out the project tokens for terminal tokens. Suckers are a transparent value-mover; the bridge
|
|
1362
|
+
// accounting is the entirety of their function.
|
|
1305
1363
|
reclaimedAmount = terminal.cashOutTokensOf({
|
|
1306
1364
|
holder: address(this),
|
|
1307
1365
|
projectId: cachedProjectId,
|
|
@@ -1309,8 +1367,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1309
1367
|
tokenToReclaim: token,
|
|
1310
1368
|
minTokensReclaimed: minTokensReclaimed,
|
|
1311
1369
|
beneficiary: payable(address(this)),
|
|
1312
|
-
metadata: bytes("")
|
|
1313
|
-
referralProjectId: 0
|
|
1370
|
+
metadata: bytes("")
|
|
1314
1371
|
});
|
|
1315
1372
|
|
|
1316
1373
|
// Sanity check to make sure we received the expected amount.
|
|
@@ -1326,7 +1383,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1326
1383
|
// Ensure the token is mapped to an address on the remote chain.
|
|
1327
1384
|
if (remoteToken.addr == bytes32(0)) revert JBSucker_TokenNotMapped(token);
|
|
1328
1385
|
|
|
1329
|
-
// Make sure that the sucker still allows sending new
|
|
1386
|
+
// Make sure that the sucker still allows sending new messages.
|
|
1330
1387
|
_requireSendingEnabled();
|
|
1331
1388
|
|
|
1332
1389
|
// Drain the outbox: read balance/nonce/root, clear balance, advance nonce and numberOfClaimsSent.
|
|
@@ -1466,7 +1523,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1466
1523
|
}
|
|
1467
1524
|
|
|
1468
1525
|
/// @notice Validates a branch root against the expected root.
|
|
1469
|
-
/// @dev This is a virtual function to allow
|
|
1526
|
+
/// @dev This is a virtual function to allow tests to override the behavior; it should never be overridden
|
|
1470
1527
|
/// otherwise.
|
|
1471
1528
|
/// @param expectedRoot The expected merkle root to validate against.
|
|
1472
1529
|
/// @param leafHash The precomputed leaf hash (`_buildTreeHash` output) for the leaf being validated.
|
|
@@ -1491,8 +1548,8 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1491
1548
|
}
|
|
1492
1549
|
}
|
|
1493
1550
|
|
|
1494
|
-
/// @notice Validates a leaf as being in the outbox merkle tree and not
|
|
1495
|
-
/// leaf as executed (to prevent double-spending).
|
|
1551
|
+
/// @notice Validates a leaf as being in the outbox merkle tree and not having been sent over the amb, and registers
|
|
1552
|
+
/// the leaf as executed (to prevent double-spending).
|
|
1496
1553
|
/// @dev Reverts if the leaf is invalid.
|
|
1497
1554
|
/// @dev IMPORTANT: Emergency exit safety depends on `numberOfClaimsSent` being accurately tracked.
|
|
1498
1555
|
/// `numberOfClaimsSent` is updated in `_sendRoot` to equal `outbox.tree.count` at the time the root is sent
|
|
@@ -1515,7 +1572,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1515
1572
|
/// @param terminalToken The terminal token that the project tokens were cashed out for.
|
|
1516
1573
|
/// @param terminalTokenAmount The amount of terminal tokens reclaimed by the cash out.
|
|
1517
1574
|
/// @param beneficiary The beneficiary of the project tokens (bytes32 for cross-VM compatibility).
|
|
1518
|
-
/// @param index The index of the leaf to prove in the terminal token's
|
|
1575
|
+
/// @param index The index of the leaf to prove in the terminal token's outbox tree.
|
|
1519
1576
|
/// @param leaves The leaves that prove that the leaf at the `index` is in the tree (i.e. the merkle branch that the
|
|
1520
1577
|
/// leaf is on).
|
|
1521
1578
|
function _validateForEmergencyExit(
|
|
@@ -1542,10 +1599,10 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1542
1599
|
}
|
|
1543
1600
|
|
|
1544
1601
|
// Check that this claim is within the bounds of who can claim.
|
|
1545
|
-
// If the root that this leaf is in was already
|
|
1602
|
+
// If the root that this leaf is in was already sent then we can not let the user claim here.
|
|
1546
1603
|
// As it could have also been received by the peer sucker, which would then let the user claim on each side.
|
|
1547
1604
|
// NOTE: We are comparing the *count* and the *index*, so `count - 1` is the last index that was sent.
|
|
1548
|
-
// A count of 0 means that no root has ever been
|
|
1605
|
+
// A count of 0 means that no root has ever been sent for this token, so everyone can claim.
|
|
1549
1606
|
JBOutboxTree storage outboxOfToken = _outboxOf[terminalToken];
|
|
1550
1607
|
if (outboxOfToken.numberOfClaimsSent != 0 && outboxOfToken.numberOfClaimsSent - 1 >= index) {
|
|
1551
1608
|
revert JBSucker_LeafAlreadyExecuted({token: terminalToken, index: index});
|
|
@@ -1553,8 +1610,8 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1553
1610
|
|
|
1554
1611
|
{
|
|
1555
1612
|
// We re-use the same `_executedFor` mapping but we use a different slot.
|
|
1556
|
-
// We can not use the regular mapping, since this claim is done for tokens being
|
|
1557
|
-
// where the regular mapping is for tokens that were
|
|
1613
|
+
// We can not use the regular mapping, since this claim is done for tokens being sent from here to the pair.
|
|
1614
|
+
// where the regular mapping is for tokens that were sent on the pair to here. Even though these may seem
|
|
1558
1615
|
// similar they are actually completely unrelated.
|
|
1559
1616
|
address emergencyExitAddress = address(bytes20(keccak256(abi.encode(terminalToken))));
|
|
1560
1617
|
|
|
@@ -1834,7 +1891,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1834
1891
|
return bytes32(uint256(uint160(addr)));
|
|
1835
1892
|
}
|
|
1836
1893
|
|
|
1837
|
-
/// @notice Allow sucker implementations to add/override mapping rules to
|
|
1894
|
+
/// @notice Allow sucker implementations to add/override mapping rules to suit their specific needs.
|
|
1838
1895
|
/// @param map The token mapping to validate.
|
|
1839
1896
|
function _validateTokenMapping(JBTokenMapping calldata map) internal pure virtual {
|
|
1840
1897
|
bool isNative = map.localToken == JBConstants.NATIVE_TOKEN;
|
package/src/JBSuckerRegistry.sol
CHANGED
|
@@ -35,10 +35,19 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
|
|
|
35
35
|
// --------------------------- custom errors ------------------------- //
|
|
36
36
|
//*********************************************************************//
|
|
37
37
|
|
|
38
|
+
/// @notice Thrown when the owner attempts to set a `toRemoteFee` greater than the maximum allowed fee.
|
|
38
39
|
error JBSuckerRegistry_FeeExceedsMax(uint256 fee, uint256 max);
|
|
40
|
+
|
|
41
|
+
/// @notice Thrown when a sucker deployment references a deployer that is not approved by this registry.
|
|
39
42
|
error JBSuckerRegistry_InvalidDeployer(IJBSuckerDeployer deployer);
|
|
43
|
+
|
|
44
|
+
/// @notice Thrown when an action references a sucker that is not registered to the given project.
|
|
40
45
|
error JBSuckerRegistry_SuckerDoesNotBelongToProject(uint256 projectId, address sucker);
|
|
46
|
+
|
|
47
|
+
/// @notice Thrown when a sucker is being removed from active listings but is not deprecated.
|
|
41
48
|
error JBSuckerRegistry_SuckerIsNotDeprecated(address sucker, JBSuckerState suckerState);
|
|
49
|
+
|
|
50
|
+
/// @notice Thrown when a sucker reports a zero peer chain ID and cannot identify a real peer chain.
|
|
42
51
|
error JBSuckerRegistry_ZeroPeerChainId(address sucker);
|
|
43
52
|
|
|
44
53
|
//*********************************************************************//
|
|
@@ -82,7 +91,7 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
|
|
|
82
91
|
//*********************************************************************//
|
|
83
92
|
|
|
84
93
|
/// @notice Tracks whether the specified sucker deployer is approved by this registry.
|
|
85
|
-
/// @custom:
|
|
94
|
+
/// @custom:param deployer The address of the deployer to check.
|
|
86
95
|
mapping(address deployer => bool) public override suckerDeployerIsAllowed;
|
|
87
96
|
|
|
88
97
|
/// @notice The ETH fee (in wei) paid into the fee project via terminal.pay() on each toRemote() call.
|
|
@@ -93,7 +102,8 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
|
|
|
93
102
|
//*********************************************************************//
|
|
94
103
|
|
|
95
104
|
/// @notice Tracks the suckers for the specified project.
|
|
96
|
-
|
|
105
|
+
/// @custom:param projectId The ID of the project whose suckers are tracked.
|
|
106
|
+
mapping(uint256 projectId => EnumerableMap.AddressToUintMap) internal _suckersOf;
|
|
97
107
|
|
|
98
108
|
//*********************************************************************//
|
|
99
109
|
// -------------------------- constructor ---------------------------- //
|
|
@@ -654,9 +664,9 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
|
|
|
654
664
|
emit SuckerDeployerAllowed({deployer: deployer, caller: _msgSender()});
|
|
655
665
|
}
|
|
656
666
|
|
|
657
|
-
/// @notice Adds multiple
|
|
667
|
+
/// @notice Adds multiple sucker deployers to the allowlist.
|
|
658
668
|
/// @dev Can only be called by this contract's owner (initially project ID 1, or JuiceboxDAO).
|
|
659
|
-
/// @param deployers The
|
|
669
|
+
/// @param deployers The addresses of the deployers to add.
|
|
660
670
|
function allowSuckerDeployers(address[] calldata deployers) public override onlyOwner {
|
|
661
671
|
// Cache _msgSender() to avoid redundant calls in the loop.
|
|
662
672
|
address sender = _msgSender();
|
|
@@ -87,16 +87,37 @@ contract JBSwapCCIPSucker is JBCCIPSucker, IUnlockCallback, IUniswapV3SwapCallba
|
|
|
87
87
|
// --------------------------- custom errors ------------------------- //
|
|
88
88
|
//*********************************************************************//
|
|
89
89
|
|
|
90
|
+
/// @notice Thrown when no received batch covers the claim's leaf index, so no conversion rate can be applied.
|
|
90
91
|
error JBSwapCCIPSucker_BatchNotReceived(uint64 nonce);
|
|
92
|
+
|
|
93
|
+
/// @notice Thrown when the Uniswap V4 unlock callback is invoked by an address other than the pool manager.
|
|
91
94
|
error JBSwapCCIPSucker_CallerNotPoolManager(address caller);
|
|
95
|
+
|
|
96
|
+
/// @notice Thrown when an incoming batch reuses a nonce that has already been received.
|
|
92
97
|
error JBSwapCCIPSucker_DuplicateBatch(uint64 nonce);
|
|
98
|
+
|
|
99
|
+
/// @notice Thrown when the bridge token is the zero address or matches the wrapped native token.
|
|
93
100
|
error JBSwapCCIPSucker_InvalidBridgeToken(address bridgeToken, address wrappedNativeToken);
|
|
101
|
+
|
|
102
|
+
/// @notice Thrown when a swap retry is attempted for a token and nonce that has no pending failed swap.
|
|
94
103
|
error JBSwapCCIPSucker_NoPendingSwap(address localToken, uint64 nonce, bool retrySwapLocked);
|
|
104
|
+
|
|
105
|
+
/// @notice Thrown when the self-only swap entry point is called by an address other than this contract.
|
|
95
106
|
error JBSwapCCIPSucker_OnlySelf(address caller, address expected);
|
|
107
|
+
|
|
108
|
+
/// @notice Thrown when an incoming root claims a positive amount but no bridge tokens were delivered with it.
|
|
96
109
|
error JBSwapCCIPSucker_PositiveRootWithoutDelivery(uint256 rootAmount);
|
|
110
|
+
|
|
111
|
+
/// @notice Thrown when a swap produces no output tokens.
|
|
97
112
|
error JBSwapCCIPSucker_SwapFailed(address tokenIn, address tokenOut, uint256 amountIn);
|
|
113
|
+
|
|
114
|
+
/// @notice Thrown when a claim or swap is attempted while another swap is already in progress.
|
|
98
115
|
error JBSwapCCIPSucker_SwapPending(uint64 nonce);
|
|
116
|
+
|
|
117
|
+
/// @notice Thrown when an incoming message delivers an unexpected number of token transfers.
|
|
99
118
|
error JBSwapCCIPSucker_UnexpectedDeliveredTokens(uint256 count);
|
|
119
|
+
|
|
120
|
+
/// @notice Thrown when the token delivered with an incoming message does not match the expected token.
|
|
100
121
|
error JBSwapCCIPSucker_WrongDeliveredToken(address delivered, address expected);
|
|
101
122
|
|
|
102
123
|
//*********************************************************************//
|
|
@@ -22,7 +22,10 @@ contract JBSwapCCIPSuckerDeployer is JBCCIPSuckerDeployer, IJBSwapCCIPSuckerDepl
|
|
|
22
22
|
// --------------------------- custom errors ------------------------- //
|
|
23
23
|
//*********************************************************************//
|
|
24
24
|
|
|
25
|
+
/// @notice Thrown when the swap configuration is set with a zero-address bridge token.
|
|
25
26
|
error JBSwapCCIPSuckerDeployer_InvalidSwapConfig(address bridgeToken);
|
|
27
|
+
|
|
28
|
+
/// @notice Thrown when the swap configuration is set after it has already been configured.
|
|
26
29
|
error JBSwapCCIPSuckerDeployer_SwapAlreadyConfigured(address bridgeToken);
|
|
27
30
|
|
|
28
31
|
//*********************************************************************//
|
|
@@ -46,12 +46,25 @@ library JBSwapPoolLib {
|
|
|
46
46
|
// --------------------------- custom errors ------------------------- //
|
|
47
47
|
//*********************************************************************//
|
|
48
48
|
|
|
49
|
+
/// @notice Thrown when a swap amount exceeds the `uint128` range required by the pool interface.
|
|
49
50
|
error JBSwapPoolLib_AmountOverflow(uint256 amount);
|
|
51
|
+
|
|
52
|
+
/// @notice Thrown when a swap callback is invoked by an address other than the expected pool.
|
|
50
53
|
error JBSwapPoolLib_CallerNotPool(address caller);
|
|
54
|
+
|
|
55
|
+
/// @notice Thrown when a pool's oracle history is too short to serve the required TWAP window.
|
|
51
56
|
error JBSwapPoolLib_InsufficientTwapHistory(address pool, uint256 availableWindow, uint256 requiredWindow);
|
|
57
|
+
|
|
58
|
+
/// @notice Thrown when the selected pool has no liquidity to swap against.
|
|
52
59
|
error JBSwapPoolLib_NoLiquidity(address pool, PoolId poolId);
|
|
60
|
+
|
|
61
|
+
/// @notice Thrown when no eligible V3 or V4 pool can be found for the given token pair.
|
|
53
62
|
error JBSwapPoolLib_NoPool(address tokenIn, address tokenOut);
|
|
63
|
+
|
|
64
|
+
/// @notice Thrown when a swap consumes less than the full requested input amount.
|
|
54
65
|
error JBSwapPoolLib_PartialFill(uint256 consumed, uint256 requested);
|
|
66
|
+
|
|
67
|
+
/// @notice Thrown when a swap's output is below the minimum acceptable amount after slippage.
|
|
55
68
|
error JBSwapPoolLib_SlippageExceeded(uint256 amountOut, uint256 minAmountOut);
|
|
56
69
|
|
|
57
70
|
//*********************************************************************//
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Archived suckers (non-canonical)
|
|
2
|
+
|
|
3
|
+
The contracts in this directory are retained for reference only. They are not part of the canonical
|
|
4
|
+
runtime surface and are out of scope for audits, the house-style sweep, and deployment. Treat any
|
|
5
|
+
inline language here as historical and do not depend on these implementations.
|
|
@@ -26,11 +26,23 @@ abstract contract JBSuckerDeployer is ERC2771Context, JBPermissioned, IJBSuckerD
|
|
|
26
26
|
// --------------------------- custom errors ------------------------- //
|
|
27
27
|
//*********************************************************************//
|
|
28
28
|
|
|
29
|
+
/// @notice Thrown when configuration is attempted but the deployer (singleton or layer-specific config) has already
|
|
30
|
+
/// been configured.
|
|
29
31
|
error JBSuckerDeployer_AlreadyConfigured(address singleton, bool layerSpecificConfigurationIsSet);
|
|
32
|
+
|
|
33
|
+
/// @notice Thrown when a sucker is deployed before the deployer's singleton implementation has been set.
|
|
30
34
|
error JBSuckerDeployer_DeployerIsNotConfigured(address singleton);
|
|
35
|
+
|
|
36
|
+
/// @notice Thrown when the layer-specific configuration is not properly set after configuration.
|
|
31
37
|
error JBSuckerDeployer_InvalidLayerSpecificConfiguration(address configurator);
|
|
38
|
+
|
|
39
|
+
/// @notice Thrown when a sucker is deployed before the layer-specific configuration has been set.
|
|
32
40
|
error JBSuckerDeployer_LayerSpecificNotConfigured(address configurator);
|
|
41
|
+
|
|
42
|
+
/// @notice Thrown when the caller is not the address authorized to configure this deployer.
|
|
33
43
|
error JBSuckerDeployer_Unauthorized(address caller, address expected);
|
|
44
|
+
|
|
45
|
+
/// @notice Thrown when the configurator address provided is the zero address.
|
|
34
46
|
error JBSuckerDeployer_ZeroConfiguratorAddress(address configurator);
|
|
35
47
|
|
|
36
48
|
//*********************************************************************//
|
|
@@ -51,9 +63,11 @@ abstract contract JBSuckerDeployer is ERC2771Context, JBPermissioned, IJBSuckerD
|
|
|
51
63
|
//*********************************************************************//
|
|
52
64
|
|
|
53
65
|
/// @notice Whether a given address was deployed by this deployer.
|
|
54
|
-
|
|
66
|
+
/// @custom:param sucker The address to check.
|
|
67
|
+
mapping(address sucker => bool) public override isSucker;
|
|
55
68
|
|
|
56
69
|
/// @notice The singleton used to clone suckers.
|
|
70
|
+
/// @dev Write-once via `configureSingleton`; the clone implementation for every sucker this deployer produces.
|
|
57
71
|
JBSucker public singleton;
|
|
58
72
|
|
|
59
73
|
//*********************************************************************//
|
|
@@ -24,6 +24,7 @@ interface IJBSucker is IERC165 {
|
|
|
24
24
|
/// @param projectTokenCount The number of project tokens claimed.
|
|
25
25
|
/// @param terminalTokenAmount The amount of terminal tokens involved.
|
|
26
26
|
/// @param index The leaf index in the inbox tree.
|
|
27
|
+
/// @param metadata The opaque, caller-defined payload carried by the claimed leaf.
|
|
27
28
|
/// @param caller The address that performed the claim.
|
|
28
29
|
event Claimed(
|
|
29
30
|
bytes32 beneficiary,
|
|
@@ -52,6 +53,7 @@ interface IJBSucker is IERC165 {
|
|
|
52
53
|
/// @param root The new outbox tree root after insertion.
|
|
53
54
|
/// @param projectTokenCount The number of project tokens cashed out.
|
|
54
55
|
/// @param terminalTokenAmount The amount of terminal tokens reclaimed.
|
|
56
|
+
/// @param metadata The opaque, caller-defined payload carried by the outbox leaf.
|
|
55
57
|
/// @param caller The address that performed the insertion.
|
|
56
58
|
event InsertToOutboxTree(
|
|
57
59
|
bytes32 indexed beneficiary,
|
|
@@ -3,6 +3,7 @@ pragma solidity 0.8.28;
|
|
|
3
3
|
|
|
4
4
|
/// @notice CCIP chain-specific constants used across Juicebox sucker contracts.
|
|
5
5
|
library CCIPHelper {
|
|
6
|
+
/// @notice Thrown when a lookup is requested for a chain ID that has no configured CCIP constants.
|
|
6
7
|
error CCIPHelper_UnsupportedChain(uint256 chainId);
|
|
7
8
|
/// @notice The CCIP router address for Ethereum mainnet.
|
|
8
9
|
address public constant ETH_ROUTER = 0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D;
|
package/src/structs/JBLeaf.sol
CHANGED
|
@@ -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
|
|
10
|
-
///
|
|
11
|
-
///
|
|
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;
|
package/src/utils/MerkleLib.sol
CHANGED
|
@@ -5,6 +5,7 @@ pragma solidity 0.8.28;
|
|
|
5
5
|
* @title MerkleLib
|
|
6
6
|
* @author Illusory Systems Inc.
|
|
7
7
|
* @notice An incremental merkle tree modeled on the eth2 deposit contract.
|
|
8
|
+
* @dev Vendored upstream library; its Doxygen-style NatSpec deviates from the repo's `///` house style.
|
|
8
9
|
*
|
|
9
10
|
*/
|
|
10
11
|
library MerkleLib {
|
|
@@ -12,6 +13,7 @@ library MerkleLib {
|
|
|
12
13
|
// --------------------------- custom errors ------------------------- //
|
|
13
14
|
//*********************************************************************//
|
|
14
15
|
|
|
16
|
+
/// @notice Thrown when a leaf is inserted into a merkle tree that has already reached its maximum number of leaves.
|
|
15
17
|
error MerkleLib_InsertTreeIsFull(uint256 size, uint256 maxLeaves);
|
|
16
18
|
|
|
17
19
|
//*********************************************************************//
|
|
@@ -82,7 +84,7 @@ library MerkleLib {
|
|
|
82
84
|
/**
|
|
83
85
|
* @notice Inserts a given node (leaf) into the merkle tree in storage.
|
|
84
86
|
* @dev Operates directly on storage, writing only the single branch entry that changes (plus count).
|
|
85
|
-
* This
|
|
87
|
+
* This writes only the changed branch slot instead of round-tripping all 33 slots through memory.
|
|
86
88
|
* @dev Reverts if the tree is already full.
|
|
87
89
|
* @param tree The storage reference to the tree.
|
|
88
90
|
* @param node Element to insert into tree.
|