@bananapus/omnichain-deployers-v6 0.0.60 → 0.0.62
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 +6 -6
- package/references/runtime.md +3 -0
- package/src/JBOmnichainDeployer.sol +96 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananapus/omnichain-deployers-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.62",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,16 +24,16 @@
|
|
|
24
24
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-omnichain-deployers-v6'"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
28
|
-
"@bananapus/core-v6": "^0.0.
|
|
27
|
+
"@bananapus/721-hook-v6": "^0.0.73",
|
|
28
|
+
"@bananapus/core-v6": "^0.0.86",
|
|
29
29
|
"@bananapus/ownable-v6": "^0.0.39",
|
|
30
30
|
"@bananapus/permission-ids-v6": "^0.0.31",
|
|
31
|
-
"@bananapus/suckers-v6": "^0.0.
|
|
31
|
+
"@bananapus/suckers-v6": "^0.0.73",
|
|
32
32
|
"@openzeppelin/contracts": "5.6.1"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@bananapus/address-registry-v6": "^0.0.
|
|
36
|
-
"@bananapus/buyback-hook-v6": "^0.0.
|
|
35
|
+
"@bananapus/address-registry-v6": "^0.0.35",
|
|
36
|
+
"@bananapus/buyback-hook-v6": "^0.0.71",
|
|
37
37
|
"@sphinx-labs/plugins": "0.33.3",
|
|
38
38
|
"@uniswap/v4-core": "1.0.2"
|
|
39
39
|
}
|
package/references/runtime.md
CHANGED
|
@@ -11,11 +11,14 @@
|
|
|
11
11
|
3. On pay, it calls the 721 hook first, then the optional extra hook with the adjusted amount context.
|
|
12
12
|
4. On cash out, it can short-circuit for suckers, otherwise it forwards into the configured hook stack in order.
|
|
13
13
|
5. Mint permission queries can be granted by suckers or by the configured extra hook.
|
|
14
|
+
6. Peer-chain adjusted account queries forward to the configured extra hook, but missing or malformed returns are
|
|
15
|
+
treated as no contribution.
|
|
14
16
|
|
|
15
17
|
## High-risk areas
|
|
16
18
|
|
|
17
19
|
- Ruleset ID prediction: if the predicted ID is wrong, hook config can be stored under the wrong key.
|
|
18
20
|
- Hook composition order: 721 logic runs before any extra hook, which affects both specs and accounting.
|
|
21
|
+
- Hook metadata decoding: split-credit metadata must satisfy the full ABI tuple minimum before it is decoded.
|
|
19
22
|
- Sucker exemptions: early-return cash-out behavior is intentional and should not be removed casually.
|
|
20
23
|
- Carry-forward logic: queueing rulesets without new tiers intentionally reuses the latest 721 hook.
|
|
21
24
|
- Meta-transaction sender handling: salt derivation uses `_msgSender()`, not raw `msg.sender`.
|
|
@@ -557,7 +557,8 @@ contract JBOmnichainDeployer is
|
|
|
557
557
|
// When issueTokensForSplits is true and splits exist, this holds the weight portion
|
|
558
558
|
// attributable to tier splits — used to prevent split credit erasure if the extra
|
|
559
559
|
// hook (e.g. buyback) returns weight=0.
|
|
560
|
-
|
|
560
|
+
// The tuple's minimum ABI encoding is 160 bytes: 4 head words plus an empty `bytes` tail.
|
|
561
|
+
if (tiered721HookSpec.metadata.length >= 160) {
|
|
561
562
|
(,,, splitCreditWeight) = abi.decode(tiered721HookSpec.metadata, (address, address, bytes, uint256));
|
|
562
563
|
}
|
|
563
564
|
}
|
|
@@ -709,12 +710,9 @@ contract JBOmnichainDeployer is
|
|
|
709
710
|
(bool success, bytes memory data) = address(extraHook.dataHook)
|
|
710
711
|
.staticcall(abi.encodeCall(IJBPeerChainAdjustedAccounts.peerChainAdjustedAccountsOf, (projectId)));
|
|
711
712
|
|
|
712
|
-
|
|
713
|
-
// and the array length. Anything shorter (an empty return, a hook with no code, or a mismatched ABI) is
|
|
714
|
-
// treated as no contribution rather than letting the decode revert.
|
|
715
|
-
if (!success || data.length < 96) return (0, new JBSourceContext[](0));
|
|
713
|
+
if (!success) return (0, new JBSourceContext[](0));
|
|
716
714
|
|
|
717
|
-
return
|
|
715
|
+
return _peerChainAdjustedAccountsFrom(data);
|
|
718
716
|
}
|
|
719
717
|
|
|
720
718
|
//*********************************************************************//
|
|
@@ -1058,6 +1056,98 @@ contract JBOmnichainDeployer is
|
|
|
1058
1056
|
return ERC2771Context._msgSender();
|
|
1059
1057
|
}
|
|
1060
1058
|
|
|
1059
|
+
/// @notice Decodes a peer-chain adjusted accounting return, falling back to no contribution if malformed.
|
|
1060
|
+
/// @param data The raw return data from an extra hook's `peerChainAdjustedAccountsOf` call.
|
|
1061
|
+
/// @return supply The extra supply to include in `sourceTotalSupply`.
|
|
1062
|
+
/// @return contexts The extra per-context surplus and balance to include in the snapshot, un-valued.
|
|
1063
|
+
function _peerChainAdjustedAccountsFrom(bytes memory data)
|
|
1064
|
+
internal
|
|
1065
|
+
pure
|
|
1066
|
+
returns (uint256 supply, JBSourceContext[] memory contexts)
|
|
1067
|
+
{
|
|
1068
|
+
// `data` is a Solidity `bytes` value. Its first memory word is the byte length, and the ABI return payload
|
|
1069
|
+
// starts at `data + 32`.
|
|
1070
|
+
//
|
|
1071
|
+
// The payload for `(uint256, JBSourceContext[])` is:
|
|
1072
|
+
// word 0: supply
|
|
1073
|
+
// word 1: offset to the dynamic `contexts` array tail, relative to the payload start
|
|
1074
|
+
// tail word 0: contexts.length
|
|
1075
|
+
// tail words: each `JBSourceContext`, encoded as 4 ABI words.
|
|
1076
|
+
//
|
|
1077
|
+
// Anything shorter than the two tuple head words plus the array-length word cannot be decoded safely.
|
|
1078
|
+
if (data.length < 96) return (0, new JBSourceContext[](0));
|
|
1079
|
+
|
|
1080
|
+
uint256 contextsOffset;
|
|
1081
|
+
assembly ("memory-safe") {
|
|
1082
|
+
// Skip the `bytes` length word, then read the first two ABI words from the payload head.
|
|
1083
|
+
supply := mload(add(data, 32))
|
|
1084
|
+
contextsOffset := mload(add(data, 64))
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// The array tail must begin after the two-word tuple head, remain ABI-word aligned, and leave room for its own
|
|
1088
|
+
// length word. If the offset points into the head, into the middle of a word, or past the buffer, a normal
|
|
1089
|
+
// `abi.decode` would revert. This wrapper instead treats the optional hook contribution as absent.
|
|
1090
|
+
if (contextsOffset < 64 || contextsOffset % 32 != 0 || contextsOffset > data.length - 32) {
|
|
1091
|
+
return (0, new JBSourceContext[](0));
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
uint256 contextCount;
|
|
1095
|
+
assembly ("memory-safe") {
|
|
1096
|
+
// The offset is relative to the payload start (`data + 32`), not the start of the `bytes` object.
|
|
1097
|
+
contextCount := mload(add(add(data, 32), contextsOffset))
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// Skip the array-length word to reach the first encoded `JBSourceContext`.
|
|
1101
|
+
uint256 contextsStart = contextsOffset + 32;
|
|
1102
|
+
// Each `JBSourceContext` has four static ABI words: token, decimals, surplus, and balance. Check the count
|
|
1103
|
+
// against the remaining bytes before allocating the array so a hostile length cannot force a large allocation
|
|
1104
|
+
// or make the loop read past the returned buffer.
|
|
1105
|
+
if (contextCount > (data.length - contextsStart) / 128) return (0, new JBSourceContext[](0));
|
|
1106
|
+
|
|
1107
|
+
contexts = new JBSourceContext[](contextCount);
|
|
1108
|
+
|
|
1109
|
+
for (uint256 i; i < contextCount; i++) {
|
|
1110
|
+
// Move to the encoded struct for this index. The offset is still payload-relative.
|
|
1111
|
+
uint256 contextOffset = contextsStart + i * 128;
|
|
1112
|
+
bytes32 token;
|
|
1113
|
+
uint256 decimals;
|
|
1114
|
+
uint256 surplus;
|
|
1115
|
+
uint256 contextBalance;
|
|
1116
|
+
|
|
1117
|
+
assembly ("memory-safe") {
|
|
1118
|
+
// Point at the first word of this encoded struct and read its four ABI words directly.
|
|
1119
|
+
let contextPointer := add(add(data, 32), contextOffset)
|
|
1120
|
+
token := mload(contextPointer)
|
|
1121
|
+
decimals := mload(add(contextPointer, 32))
|
|
1122
|
+
surplus := mload(add(contextPointer, 64))
|
|
1123
|
+
contextBalance := mload(add(contextPointer, 96))
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// The ABI decoder would reject values that do not fit their declared Solidity types. Because this function
|
|
1127
|
+
// decodes manually, it must enforce the same bounds before casting so malformed data cannot silently
|
|
1128
|
+
// truncate into `uint8` or `uint128`.
|
|
1129
|
+
if (decimals > type(uint8).max || surplus > type(uint128).max || contextBalance > type(uint128).max) {
|
|
1130
|
+
return (0, new JBSourceContext[](0));
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// Store the checked values using the struct's real types. At this point every read was inside the buffer
|
|
1134
|
+
// and every narrowed cast has been proven safe.
|
|
1135
|
+
// Casting to `uint8` is safe because the guard above rejected larger values.
|
|
1136
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1137
|
+
uint8 checkedDecimals = uint8(decimals);
|
|
1138
|
+
// Casting to `uint128` is safe because the guard above rejected larger values.
|
|
1139
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1140
|
+
uint128 checkedSurplus = uint128(surplus);
|
|
1141
|
+
// Casting to `uint128` is safe because the guard above rejected larger values.
|
|
1142
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1143
|
+
uint128 checkedBalance = uint128(contextBalance);
|
|
1144
|
+
|
|
1145
|
+
contexts[i] = JBSourceContext({
|
|
1146
|
+
token: token, decimals: checkedDecimals, surplus: checkedSurplus, balance: checkedBalance
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1061
1151
|
/// @notice Revert unless the trusted directory records `CONTROLLER` for `projectId`.
|
|
1062
1152
|
/// @dev Use `allowUnset = true` as a pre-launch check: a fresh project with no controller wired yet is accepted.
|
|
1063
1153
|
/// Use `allowUnset = false` as a post-launch check: `CONTROLLER` must be live in the directory.
|