@bananapus/suckers-v6 0.0.75 → 0.0.76
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/package.json
CHANGED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
|
+
|
|
4
|
+
import {JBSourceContext} from "../structs/JBSourceContext.sol";
|
|
5
|
+
|
|
6
|
+
/// @notice Helpers for reading optional `IJBPeerChainAdjustedAccounts` return data.
|
|
7
|
+
library JBPeerChainAdjustedAccountsLib {
|
|
8
|
+
/// @notice Decodes peer-chain adjusted accounting return data, falling back to no contribution if malformed.
|
|
9
|
+
/// @param data The raw return data from a `peerChainAdjustedAccountsOf` call.
|
|
10
|
+
/// @return supply The extra supply to include in `sourceTotalSupply`.
|
|
11
|
+
/// @return contexts The extra per-context surplus and balance to include in the snapshot, un-valued.
|
|
12
|
+
function decode(bytes memory data) internal pure returns (uint256 supply, JBSourceContext[] memory contexts) {
|
|
13
|
+
// `data` is a Solidity `bytes` value. In memory, its first word is the byte length, and the hook's ABI return
|
|
14
|
+
// payload begins one word later at `data + 32`.
|
|
15
|
+
//
|
|
16
|
+
// The payload for `(uint256, JBSourceContext[])` is:
|
|
17
|
+
// word 0: supply
|
|
18
|
+
// word 1: offset to the dynamic `contexts` array tail, relative to the payload start
|
|
19
|
+
// tail word 0: contexts.length
|
|
20
|
+
// tail words: each `JBSourceContext`, encoded as 4 ABI words.
|
|
21
|
+
//
|
|
22
|
+
// A valid return needs at least the two-word tuple head plus the array-length word. Anything shorter would
|
|
23
|
+
// make the reads below point outside the returned buffer, so the optional contribution is ignored.
|
|
24
|
+
if (data.length < 96) return (0, new JBSourceContext[](0));
|
|
25
|
+
|
|
26
|
+
// The tuple head is fixed-width, so read it directly instead of `abi.decode`:
|
|
27
|
+
// - `supply` is word 0 of the ABI payload.
|
|
28
|
+
// - `contextsOffset` is word 1 and points to the dynamic-array tail.
|
|
29
|
+
// Manual reads let malformed optional hooks fail soft instead of reverting the whole snapshot.
|
|
30
|
+
uint256 contextsOffset;
|
|
31
|
+
assembly ("memory-safe") {
|
|
32
|
+
// Skip the `bytes` length word and read payload word 0.
|
|
33
|
+
supply := mload(add(data, 32))
|
|
34
|
+
// Read payload word 1. The value is payload-relative, not memory-object-relative.
|
|
35
|
+
contextsOffset := mload(add(data, 64))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// The dynamic array tail must start after the two-word tuple head (`>= 64`), be ABI-word aligned, and leave
|
|
39
|
+
// room for its own length word. If any of these fail, `abi.decode` would revert; this helper treats the
|
|
40
|
+
// optional hook as absent.
|
|
41
|
+
if (contextsOffset < 64 || contextsOffset % 32 != 0 || contextsOffset > data.length - 32) {
|
|
42
|
+
return (0, new JBSourceContext[](0));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// `contextsOffset` was proven to leave room for the array length word, so this read is in-bounds. Add `32` to
|
|
46
|
+
// `data` first because offsets are relative to the payload start, not the `bytes` length word.
|
|
47
|
+
uint256 contextCount;
|
|
48
|
+
assembly ("memory-safe") {
|
|
49
|
+
contextCount := mload(add(add(data, 32), contextsOffset))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Skip the array-length word to reach the first encoded `JBSourceContext`.
|
|
53
|
+
uint256 contextsStart = contextsOffset + 32;
|
|
54
|
+
// Each `JBSourceContext` is four ABI words: token, decimals, surplus, balance. This bounds check prevents both
|
|
55
|
+
// oversized allocation from a hostile length word and out-of-bounds reads in the loop.
|
|
56
|
+
if (contextCount > (data.length - contextsStart) / 128) return (0, new JBSourceContext[](0));
|
|
57
|
+
|
|
58
|
+
// Only allocate after proving the claimed array length fits inside the returned bytes.
|
|
59
|
+
contexts = new JBSourceContext[](contextCount);
|
|
60
|
+
|
|
61
|
+
for (uint256 i; i < contextCount; i++) {
|
|
62
|
+
// Move to the encoded struct for this index. The multiplication is safe because `contextCount` already
|
|
63
|
+
// proved every 128-byte struct fits in the buffer.
|
|
64
|
+
uint256 contextOffset = contextsStart + i * 128;
|
|
65
|
+
// Read narrowed fields as full words first. The ABI decoder would reject out-of-range values for
|
|
66
|
+
// `uint8`/`uint128`, so the manual decoder must check those ranges before casting.
|
|
67
|
+
bytes32 token;
|
|
68
|
+
uint256 decimals;
|
|
69
|
+
uint256 surplus;
|
|
70
|
+
uint256 contextBalance;
|
|
71
|
+
|
|
72
|
+
assembly ("memory-safe") {
|
|
73
|
+
// Point at the first word of the encoded `JBSourceContext`.
|
|
74
|
+
let contextPointer := add(add(data, 32), contextOffset)
|
|
75
|
+
// Struct word 0: source-local token, padded to bytes32.
|
|
76
|
+
token := mload(contextPointer)
|
|
77
|
+
// Struct word 1: decimal precision, encoded as a full ABI word.
|
|
78
|
+
decimals := mload(add(contextPointer, 32))
|
|
79
|
+
// Struct word 2: raw surplus in the context's own decimals.
|
|
80
|
+
surplus := mload(add(contextPointer, 64))
|
|
81
|
+
// Struct word 3: raw recorded balance in the context's own decimals.
|
|
82
|
+
contextBalance := mload(add(contextPointer, 96))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Mirror ABI decoder type checks before narrowing. Returning `(0, [])` avoids silently truncating a
|
|
86
|
+
// malformed hook's values into smaller wire types.
|
|
87
|
+
if (decimals > type(uint8).max || surplus > type(uint128).max || contextBalance > type(uint128).max) {
|
|
88
|
+
return (0, new JBSourceContext[](0));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Casting is safe because the guard above rejected larger values, but forge lint cannot infer that.
|
|
92
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
93
|
+
uint8 checkedDecimals = uint8(decimals);
|
|
94
|
+
// Casting is safe for the same reason as `checkedDecimals`.
|
|
95
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
96
|
+
uint128 checkedSurplus = uint128(surplus);
|
|
97
|
+
// Casting is safe for the same reason as `checkedDecimals`.
|
|
98
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
99
|
+
uint128 checkedBalance = uint128(contextBalance);
|
|
100
|
+
|
|
101
|
+
// Store the checked values using the struct's actual wire types. At this point every memory read was
|
|
102
|
+
// inside the buffer and every narrowed cast has been proven safe.
|
|
103
|
+
contexts[i] = JBSourceContext({
|
|
104
|
+
token: token, decimals: checkedDecimals, surplus: checkedSurplus, balance: checkedBalance
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -12,6 +12,7 @@ import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadat
|
|
|
12
12
|
import {IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
|
|
13
13
|
|
|
14
14
|
import {IJBPeerChainAdjustedAccounts} from "../interfaces/IJBPeerChainAdjustedAccounts.sol";
|
|
15
|
+
import {JBPeerChainAdjustedAccountsLib} from "./JBPeerChainAdjustedAccountsLib.sol";
|
|
15
16
|
import {JBInboxTreeRoot} from "../structs/JBInboxTreeRoot.sol";
|
|
16
17
|
import {JBMessageRoot} from "../structs/JBMessageRoot.sol";
|
|
17
18
|
import {JBSourceContext} from "../structs/JBSourceContext.sol";
|
|
@@ -249,15 +250,13 @@ library JBSuckerLib {
|
|
|
249
250
|
address dataHook = ruleset.dataHook();
|
|
250
251
|
if (dataHook == address(0) || dataHook.code.length == 0) return (0, new JBSourceContext[](0));
|
|
251
252
|
|
|
252
|
-
// Ask the hook for any off-terminal supply and per-context surplus/balance.
|
|
253
|
-
//
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return (0, new JBSourceContext[](0));
|
|
260
|
-
}
|
|
253
|
+
// Ask the hook for any off-terminal supply and per-context surplus/balance. Non-supporting, broken, or
|
|
254
|
+
// malformed hooks are ignored so the baseline snapshot still goes out.
|
|
255
|
+
(bool hookCallSucceeded, bytes memory hookData) =
|
|
256
|
+
dataHook.staticcall(abi.encodeCall(IJBPeerChainAdjustedAccounts.peerChainAdjustedAccountsOf, (projectId)));
|
|
257
|
+
if (!hookCallSucceeded) return (0, new JBSourceContext[](0));
|
|
258
|
+
|
|
259
|
+
return JBPeerChainAdjustedAccountsLib.decode(hookData);
|
|
261
260
|
}
|
|
262
261
|
|
|
263
262
|
/// @notice Reads one accounting context's raw surplus and balance into a `JBSourceContext`, performing no price
|