@bananapus/suckers-v6 0.0.47 → 0.0.49

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.
@@ -38,6 +38,56 @@ library JBSuckerLib {
38
38
  /// @notice The ETH decimal precision used for cross-chain peer snapshots.
39
39
  uint8 internal constant _ETH_DECIMALS = 18;
40
40
 
41
+ //*********************************************************************//
42
+ // ---------------------- external transactions ---------------------- //
43
+ //*********************************************************************//
44
+
45
+ /// @notice Build the cross-chain snapshot message (total supply, surplus, balance).
46
+ /// @dev Extracted from `JBSucker._buildSnapshotAndSend` to reduce child contract bytecode.
47
+ /// Called via DELEGATECALL. Includes ETH aggregate computation inline (cannot call own external fns).
48
+ /// @param directory The JB directory to look up controllers and terminals.
49
+ /// @param prices The price oracle to use for non-ETH terminal-token balances.
50
+ /// @param projectId The project ID.
51
+ /// @param remoteToken The remote token bytes32 address.
52
+ /// @param amount The amount of terminal tokens to bridge.
53
+ /// @param nonce The outbox nonce for this send.
54
+ /// @param root The merkle root of the outbox tree.
55
+ /// @param messageVersion The message format version.
56
+ /// @param sourceTimestamp The monotonic source freshness key for this snapshot.
57
+ /// @return message The constructed JBMessageRoot.
58
+ function buildSnapshotMessage(
59
+ IJBDirectory directory,
60
+ IJBPrices prices,
61
+ uint256 projectId,
62
+ bytes32 remoteToken,
63
+ uint256 amount,
64
+ uint64 nonce,
65
+ bytes32 root,
66
+ uint8 messageVersion,
67
+ uint256 sourceTimestamp
68
+ )
69
+ external
70
+ view
71
+ returns (JBMessageRoot memory message)
72
+ {
73
+ (uint256 localTotalSupply, uint256 ethSurplus, uint256 ethBalance) =
74
+ _snapshotAccountsOf({directory: directory, prices: prices, projectId: projectId});
75
+
76
+ // Construct the cross-chain message with the snapshot data.
77
+ message = JBMessageRoot({
78
+ version: messageVersion,
79
+ token: remoteToken,
80
+ amount: amount,
81
+ remoteRoot: JBInboxTreeRoot({nonce: nonce, root: root}),
82
+ sourceTotalSupply: localTotalSupply,
83
+ sourceCurrency: JBCurrencyIds.ETH,
84
+ sourceDecimals: _ETH_DECIMALS,
85
+ sourceSurplus: ethSurplus,
86
+ sourceBalance: ethBalance,
87
+ sourceTimestamp: sourceTimestamp
88
+ });
89
+ }
90
+
41
91
  //*********************************************************************//
42
92
  // ------------------------- external views -------------------------- //
43
93
  //*********************************************************************//
@@ -61,8 +111,114 @@ library JBSuckerLib {
61
111
  return _buildETHAggregateInternal({directory: directory, projectId: projectId, prices: prices});
62
112
  }
63
113
 
114
+ /// @notice Compute a branch root from a leaf, branch, and index. Wraps MerkleLib.branchRoot so its
115
+ /// ~170 lines of unrolled assembly live in the library's bytecode instead of each sucker's.
116
+ /// @param item The leaf hash.
117
+ /// @param branch The 32-element merkle proof branch.
118
+ /// @param index The leaf index.
119
+ /// @return The computed merkle root.
120
+ function computeBranchRoot(bytes32 item, bytes32[32] memory branch, uint256 index) external pure returns (bytes32) {
121
+ // Delegate to MerkleLib's unrolled assembly implementation.
122
+ return MerkleLib.branchRoot({item: item, branch: branch, index: index});
123
+ }
124
+
125
+ /// @notice Compute the merkle tree root from branch and count. Loop-based replacement for the unrolled
126
+ /// MerkleLib.root() — saves ~3KB per sucker when called via DELEGATECALL instead of inlining.
127
+ /// @param branch The 32-element branch array (caller copies from storage to memory).
128
+ /// @param count The number of leaves inserted into the tree.
129
+ /// @return current The merkle root.
130
+ function computeTreeRoot(bytes32[32] memory branch, uint256 count) external pure returns (bytes32 current) {
131
+ // An empty tree has a well-known root.
132
+ if (count == 0) return MerkleLib.Z_32;
133
+
134
+ // solhint-disable-next-line no-inline-assembly
135
+ assembly ("memory-safe") {
136
+ // Build zero hashes on-the-fly: Z[0] = 0, Z[i+1] = keccak256(Z[i], Z[i]).
137
+ let zPtr := mload(0x40)
138
+ mstore(0x40, add(zPtr, 0x420)) // 33 slots × 32 bytes
139
+ mstore(zPtr, 0) // Z[0] = bytes32(0)
140
+ for { let j := 0 } lt(j, 32) { j := add(j, 1) } {
141
+ let prev := mload(add(zPtr, mul(j, 0x20)))
142
+ mstore(0x00, prev)
143
+ mstore(0x20, prev)
144
+ mstore(add(zPtr, mul(add(j, 1), 0x20)), keccak256(0x00, 0x40))
145
+ }
146
+
147
+ // Walk bits of `count` from LSB to MSB.
148
+ // First set bit → initialize current = keccak256(branch[i], Z[i]).
149
+ // Each subsequent level → merge branch[i] or Z[i] with current.
150
+ let started := 0
151
+ for { let i := 0 } lt(i, 32) { i := add(i, 1) } {
152
+ switch started
153
+ case 0 {
154
+ if and(count, shl(i, 1)) {
155
+ mstore(0x00, mload(add(branch, mul(i, 0x20))))
156
+ mstore(0x20, mload(add(zPtr, mul(i, 0x20))))
157
+ current := keccak256(0x00, 0x40)
158
+ started := 1
159
+ }
160
+ }
161
+ default {
162
+ switch and(count, shl(i, 1))
163
+ case 0 {
164
+ mstore(0x00, current)
165
+ mstore(0x20, mload(add(zPtr, mul(i, 0x20))))
166
+ }
167
+ default {
168
+ mstore(0x00, mload(add(branch, mul(i, 0x20))))
169
+ mstore(0x20, current)
170
+ }
171
+ current := keccak256(0x00, 0x40)
172
+ }
173
+ }
174
+ }
175
+ }
176
+
177
+ /// @notice Convert a peer chain snapshot value to the requested currency and decimal precision.
178
+ /// @param prices The price oracle to use when currency conversion is needed.
179
+ /// @param projectId The project ID.
180
+ /// @param source The peer chain snapshot containing value, currency, and decimals.
181
+ /// @param decimals The target decimal precision.
182
+ /// @param currency The target currency.
183
+ /// @return converted The converted value.
184
+ function convertPeerValue(
185
+ IJBPrices prices,
186
+ uint256 projectId,
187
+ JBDenominatedAmount memory source,
188
+ uint256 decimals,
189
+ uint256 currency
190
+ )
191
+ external
192
+ view
193
+ returns (uint256 converted)
194
+ {
195
+ // Nothing to convert if the source value is zero.
196
+ if (source.value == 0) return 0;
197
+
198
+ // If the source currency matches the target, just adjust decimals.
199
+ // forge-lint: disable-next-line(unsafe-typecast)
200
+ if (source.currency == uint32(currency)) {
201
+ converted = JBFixedPointNumber.adjustDecimals({
202
+ value: source.value, decimals: source.decimals, targetDecimals: decimals
203
+ });
204
+ } else {
205
+ // Convert using the price oracle.
206
+ try prices.pricePerUnitOf({
207
+ projectId: projectId,
208
+ pricingCurrency: source.currency,
209
+ // forge-lint: disable-next-line(unsafe-typecast)
210
+ unitCurrency: uint32(currency),
211
+ decimals: source.decimals
212
+ }) returns (
213
+ uint256 price
214
+ ) {
215
+ converted = mulDiv({x: source.value, y: 10 ** decimals, denominator: price});
216
+ } catch {}
217
+ }
218
+ }
219
+
64
220
  //*********************************************************************//
65
- // ----------------------- internal helpers -------------------------- //
221
+ // ------------------------- internal views -------------------------- //
66
222
  //*********************************************************************//
67
223
 
68
224
  /// @dev Shared implementation for ETH aggregate. Internal so it can be called from other
@@ -151,49 +307,6 @@ library JBSuckerLib {
151
307
  }
152
308
  }
153
309
 
154
- /// @notice Convert a peer chain snapshot value to the requested currency and decimal precision.
155
- /// @param prices The price oracle to use when currency conversion is needed.
156
- /// @param projectId The project ID.
157
- /// @param source The peer chain snapshot containing value, currency, and decimals.
158
- /// @param decimals The target decimal precision.
159
- /// @param currency The target currency.
160
- /// @return converted The converted value.
161
- function convertPeerValue(
162
- IJBPrices prices,
163
- uint256 projectId,
164
- JBDenominatedAmount memory source,
165
- uint256 decimals,
166
- uint256 currency
167
- )
168
- external
169
- view
170
- returns (uint256 converted)
171
- {
172
- // Nothing to convert if the source value is zero.
173
- if (source.value == 0) return 0;
174
-
175
- // If the source currency matches the target, just adjust decimals.
176
- // forge-lint: disable-next-line(unsafe-typecast)
177
- if (source.currency == uint32(currency)) {
178
- converted = JBFixedPointNumber.adjustDecimals({
179
- value: source.value, decimals: source.decimals, targetDecimals: decimals
180
- });
181
- } else {
182
- // Convert using the price oracle.
183
- try prices.pricePerUnitOf({
184
- projectId: projectId,
185
- pricingCurrency: source.currency,
186
- // forge-lint: disable-next-line(unsafe-typecast)
187
- unitCurrency: uint32(currency),
188
- decimals: source.decimals
189
- }) returns (
190
- uint256 price
191
- ) {
192
- converted = mulDiv({x: source.value, y: 10 ** decimals, denominator: price});
193
- } catch {}
194
- }
195
- }
196
-
197
310
  /// @notice Return the project's controller if it exists and advertises the controller interface.
198
311
  /// @param directory The JB directory to look up the controller.
199
312
  /// @param projectId The project ID.
@@ -208,56 +321,6 @@ library JBSuckerLib {
208
321
  } catch {}
209
322
  }
210
323
 
211
- //*********************************************************************//
212
- // -------------------- external state-changing ---------------------- //
213
- //*********************************************************************//
214
-
215
- /// @notice Build the cross-chain snapshot message (total supply, surplus, balance).
216
- /// @dev Extracted from `JBSucker._buildSnapshotAndSend` to reduce child contract bytecode.
217
- /// Called via DELEGATECALL. Includes ETH aggregate computation inline (cannot call own external fns).
218
- /// @param directory The JB directory to look up controllers and terminals.
219
- /// @param prices The price oracle to use for non-ETH terminal-token balances.
220
- /// @param projectId The project ID.
221
- /// @param remoteToken The remote token bytes32 address.
222
- /// @param amount The amount of terminal tokens to bridge.
223
- /// @param nonce The outbox nonce for this send.
224
- /// @param root The merkle root of the outbox tree.
225
- /// @param messageVersion The message format version.
226
- /// @param sourceTimestamp The monotonic source freshness key for this snapshot.
227
- /// @return message The constructed JBMessageRoot.
228
- function buildSnapshotMessage(
229
- IJBDirectory directory,
230
- IJBPrices prices,
231
- uint256 projectId,
232
- bytes32 remoteToken,
233
- uint256 amount,
234
- uint64 nonce,
235
- bytes32 root,
236
- uint8 messageVersion,
237
- uint256 sourceTimestamp
238
- )
239
- external
240
- view
241
- returns (JBMessageRoot memory message)
242
- {
243
- (uint256 localTotalSupply, uint256 ethSurplus, uint256 ethBalance) =
244
- _snapshotAccountsOf({directory: directory, prices: prices, projectId: projectId});
245
-
246
- // Construct the cross-chain message with the snapshot data.
247
- message = JBMessageRoot({
248
- version: messageVersion,
249
- token: remoteToken,
250
- amount: amount,
251
- remoteRoot: JBInboxTreeRoot({nonce: nonce, root: root}),
252
- sourceTotalSupply: localTotalSupply,
253
- sourceCurrency: JBCurrencyIds.ETH,
254
- sourceDecimals: _ETH_DECIMALS,
255
- sourceSurplus: ethSurplus,
256
- sourceBalance: ethBalance,
257
- sourceTimestamp: sourceTimestamp
258
- });
259
- }
260
-
261
324
  /// @notice Optional project-specific adjusted accounts to add to peer-chain snapshots.
262
325
  /// @dev Reads the current ruleset's data hook and asks it for extra supply/surplus. Non-supporting hooks,
263
326
  /// broken hooks, and short return data are ignored so a project's baseline snapshot remains usable.
@@ -337,71 +400,4 @@ library JBSuckerLib {
337
400
  ethBalance += additionalBalance;
338
401
  }
339
402
  }
340
-
341
- //*********************************************************************//
342
- // -------------------- merkle tree helpers -------------------------- //
343
- //*********************************************************************//
344
-
345
- /// @notice Compute the merkle tree root from branch and count. Loop-based replacement for the unrolled
346
- /// MerkleLib.root() — saves ~3KB per sucker when called via DELEGATECALL instead of inlining.
347
- /// @param branch The 32-element branch array (caller copies from storage to memory).
348
- /// @param count The number of leaves inserted into the tree.
349
- /// @return current The merkle root.
350
- function computeTreeRoot(bytes32[32] memory branch, uint256 count) external pure returns (bytes32 current) {
351
- // An empty tree has a well-known root.
352
- if (count == 0) return MerkleLib.Z_32;
353
-
354
- // solhint-disable-next-line no-inline-assembly
355
- assembly ("memory-safe") {
356
- // Build zero hashes on-the-fly: Z[0] = 0, Z[i+1] = keccak256(Z[i], Z[i]).
357
- let zPtr := mload(0x40)
358
- mstore(0x40, add(zPtr, 0x420)) // 33 slots × 32 bytes
359
- mstore(zPtr, 0) // Z[0] = bytes32(0)
360
- for { let j := 0 } lt(j, 32) { j := add(j, 1) } {
361
- let prev := mload(add(zPtr, mul(j, 0x20)))
362
- mstore(0x00, prev)
363
- mstore(0x20, prev)
364
- mstore(add(zPtr, mul(add(j, 1), 0x20)), keccak256(0x00, 0x40))
365
- }
366
-
367
- // Walk bits of `count` from LSB to MSB.
368
- // First set bit → initialize current = keccak256(branch[i], Z[i]).
369
- // Each subsequent level → merge branch[i] or Z[i] with current.
370
- let started := 0
371
- for { let i := 0 } lt(i, 32) { i := add(i, 1) } {
372
- switch started
373
- case 0 {
374
- if and(count, shl(i, 1)) {
375
- mstore(0x00, mload(add(branch, mul(i, 0x20))))
376
- mstore(0x20, mload(add(zPtr, mul(i, 0x20))))
377
- current := keccak256(0x00, 0x40)
378
- started := 1
379
- }
380
- }
381
- default {
382
- switch and(count, shl(i, 1))
383
- case 0 {
384
- mstore(0x00, current)
385
- mstore(0x20, mload(add(zPtr, mul(i, 0x20))))
386
- }
387
- default {
388
- mstore(0x00, mload(add(branch, mul(i, 0x20))))
389
- mstore(0x20, current)
390
- }
391
- current := keccak256(0x00, 0x40)
392
- }
393
- }
394
- }
395
- }
396
-
397
- /// @notice Compute a branch root from a leaf, branch, and index. Wraps MerkleLib.branchRoot so its
398
- /// ~170 lines of unrolled assembly live in the library's bytecode instead of each sucker's.
399
- /// @param item The leaf hash.
400
- /// @param branch The 32-element merkle proof branch.
401
- /// @param index The leaf index.
402
- /// @return The computed merkle root.
403
- function computeBranchRoot(bytes32 item, bytes32[32] memory branch, uint256 index) external pure returns (bytes32) {
404
- // Delegate to MerkleLib's unrolled assembly implementation.
405
- return MerkleLib.branchRoot({_item: item, _branch: branch, _index: index});
406
- }
407
403
  }