@bananapus/suckers-v6 1.0.0 → 1.0.2
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 +2 -1
- package/package.json +1 -1
- package/references/entrypoints.md +6 -2
- package/references/operations.md +3 -1
- package/references/runtime.md +2 -2
- package/src/JBSucker.sol +42 -51
- package/src/JBSuckerRegistry.sol +319 -170
- package/src/interfaces/IJBSucker.sol +8 -5
- package/src/interfaces/IJBSuckerRegistry.sol +70 -0
- package/src/libraries/JBSuckerLib.sol +16 -15
- package/src/structs/JBChainAccounting.sol +13 -14
- package/src/structs/JBPeerChainContext.sol +4 -4
- package/src/structs/JBSourceContext.sol +7 -10
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
|
-
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
5
4
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
6
5
|
import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
7
6
|
import {IJBTokens} from "@bananapus/core-v6/src/interfaces/IJBTokens.sol";
|
|
7
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
8
|
+
|
|
9
|
+
import {JBSuckerState} from "../enums/JBSuckerState.sol";
|
|
10
|
+
|
|
8
11
|
import {JBAccountingSnapshot} from "../structs/JBAccountingSnapshot.sol";
|
|
9
12
|
import {JBChainAccounting} from "../structs/JBChainAccounting.sol";
|
|
10
13
|
import {JBClaim} from "../structs/JBClaim.sol";
|
|
@@ -13,7 +16,6 @@ import {JBOutboxTree} from "../structs/JBOutboxTree.sol";
|
|
|
13
16
|
import {JBPeerChainContext} from "../structs/JBPeerChainContext.sol";
|
|
14
17
|
import {JBPeerChainValue} from "../structs/JBPeerChainValue.sol";
|
|
15
18
|
import {JBRemoteToken} from "../structs/JBRemoteToken.sol";
|
|
16
|
-
import {JBSuckerState} from "../enums/JBSuckerState.sol";
|
|
17
19
|
import {JBTokenMapping} from "../structs/JBTokenMapping.sol";
|
|
18
20
|
|
|
19
21
|
/// @notice The minimal interface for a sucker contract.
|
|
@@ -158,10 +160,11 @@ interface IJBSucker is IERC165 {
|
|
|
158
160
|
/// @return The peer address.
|
|
159
161
|
function peer() external view returns (bytes32);
|
|
160
162
|
|
|
161
|
-
/// @notice The
|
|
163
|
+
/// @notice The un-valued accounting record this sucker holds for every peer chain it has heard about.
|
|
162
164
|
/// @dev The registry reads this to gather a project's full cross-chain knowledge and re-gossip it. Records are
|
|
163
|
-
///
|
|
164
|
-
///
|
|
165
|
+
/// keyed to this sucker's local token when a source token matches a remote-token mapping. The next receiver only
|
|
166
|
+
/// needs its mapping to this peer's token.
|
|
167
|
+
/// @return accounts One accounting record per known peer chain.
|
|
165
168
|
function peerChainAccountsOf() external view returns (JBChainAccounting[] memory accounts);
|
|
166
169
|
|
|
167
170
|
/// @notice One peer chain's per-currency surplus and balance from its latest accepted record, plus its freshness
|
|
@@ -35,6 +35,20 @@ interface IJBSuckerRegistry {
|
|
|
35
35
|
/// @param caller The address that removed the sucker.
|
|
36
36
|
event SuckerDeprecated(uint256 projectId, address sucker, address caller);
|
|
37
37
|
|
|
38
|
+
/// @notice Emitted when a local-to-remote token mapping is added to the allowlist.
|
|
39
|
+
/// @param localToken The local token address.
|
|
40
|
+
/// @param remoteChainId The ID of the remote chain.
|
|
41
|
+
/// @param remoteToken The remote token address encoded as bytes32.
|
|
42
|
+
/// @param caller The address that allowed the token mapping.
|
|
43
|
+
event TokenMappingAllowed(address localToken, uint256 remoteChainId, bytes32 remoteToken, address caller);
|
|
44
|
+
|
|
45
|
+
/// @notice Emitted when a local-to-remote token mapping is removed from the allowlist.
|
|
46
|
+
/// @param localToken The local token address.
|
|
47
|
+
/// @param remoteChainId The ID of the remote chain.
|
|
48
|
+
/// @param remoteToken The remote token address encoded as bytes32.
|
|
49
|
+
/// @param caller The address that removed the token mapping.
|
|
50
|
+
event TokenMappingRemoved(address localToken, uint256 remoteChainId, bytes32 remoteToken, address caller);
|
|
51
|
+
|
|
38
52
|
/// @notice Emitted when the toRemoteFee is changed.
|
|
39
53
|
/// @param oldFee The previous fee.
|
|
40
54
|
/// @param newFee The new fee.
|
|
@@ -90,6 +104,14 @@ interface IJBSuckerRegistry {
|
|
|
90
104
|
/// @return totalSupply The combined peer chain total supply.
|
|
91
105
|
function remoteTotalSupplyOf(uint256 projectId) external view returns (uint256 totalSupply);
|
|
92
106
|
|
|
107
|
+
/// @notice Reverts unless a token mapping can be chosen by a project.
|
|
108
|
+
/// @dev Disable mappings never require owner approval. Non-native same-address mappings pass through directly.
|
|
109
|
+
/// Native-to-native and differing-address mappings must be explicitly allowed.
|
|
110
|
+
/// @param localToken The local token address.
|
|
111
|
+
/// @param remoteChainId The ID of the remote chain.
|
|
112
|
+
/// @param remoteToken The remote token address encoded as bytes32.
|
|
113
|
+
function requireTokenMappingAllowed(address localToken, uint256 remoteChainId, bytes32 remoteToken) external view;
|
|
114
|
+
|
|
93
115
|
/// @notice Whether the specified sucker deployer is approved by this registry.
|
|
94
116
|
/// @param deployer The address of the deployer to check.
|
|
95
117
|
/// @return Whether the deployer is allowed.
|
|
@@ -105,6 +127,20 @@ interface IJBSuckerRegistry {
|
|
|
105
127
|
/// @return The addresses of the suckers.
|
|
106
128
|
function suckersOf(uint256 projectId) external view returns (address[] memory);
|
|
107
129
|
|
|
130
|
+
/// @notice Whether a local-to-remote token mapping is approved by this registry.
|
|
131
|
+
/// @param localToken The local token address.
|
|
132
|
+
/// @param remoteChainId The ID of the remote chain.
|
|
133
|
+
/// @param remoteToken The remote token address encoded as bytes32.
|
|
134
|
+
/// @return Whether the token mapping is allowed.
|
|
135
|
+
function tokenMappingIsAllowed(
|
|
136
|
+
address localToken,
|
|
137
|
+
uint256 remoteChainId,
|
|
138
|
+
bytes32 remoteToken
|
|
139
|
+
)
|
|
140
|
+
external
|
|
141
|
+
view
|
|
142
|
+
returns (bool);
|
|
143
|
+
|
|
108
144
|
/// @notice The ETH fee (in wei) paid into the fee project on each toRemote() call.
|
|
109
145
|
/// @return The current fee.
|
|
110
146
|
function toRemoteFee() external view returns (uint256);
|
|
@@ -153,6 +189,23 @@ interface IJBSuckerRegistry {
|
|
|
153
189
|
/// @param deployers The addresses of the deployers to allow.
|
|
154
190
|
function allowSuckerDeployers(address[] calldata deployers) external;
|
|
155
191
|
|
|
192
|
+
/// @notice Add a local-to-remote token mapping to the allowlist.
|
|
193
|
+
/// @param localToken The local token address.
|
|
194
|
+
/// @param remoteChainId The ID of the remote chain.
|
|
195
|
+
/// @param remoteToken The remote token address encoded as bytes32.
|
|
196
|
+
function allowTokenMapping(address localToken, uint256 remoteChainId, bytes32 remoteToken) external;
|
|
197
|
+
|
|
198
|
+
/// @notice Add multiple local-to-remote token mappings to the allowlist.
|
|
199
|
+
/// @param localTokens The local token addresses.
|
|
200
|
+
/// @param remoteChainIds The remote chain IDs.
|
|
201
|
+
/// @param remoteTokens The remote token addresses encoded as bytes32.
|
|
202
|
+
function allowTokenMappings(
|
|
203
|
+
address[] calldata localTokens,
|
|
204
|
+
uint256[] calldata remoteChainIds,
|
|
205
|
+
bytes32[] calldata remoteTokens
|
|
206
|
+
)
|
|
207
|
+
external;
|
|
208
|
+
|
|
156
209
|
/// @notice Deploy one or more suckers for the specified project.
|
|
157
210
|
/// @dev This call also applies each configuration's token mappings on the deployed suckers. `DEPLOY_SUCKERS`
|
|
158
211
|
/// authorizes those initial mappings; use `MAP_SUCKER_TOKEN` for post-deployment mapping changes.
|
|
@@ -177,6 +230,23 @@ interface IJBSuckerRegistry {
|
|
|
177
230
|
/// @param deployer The address of the deployer to remove.
|
|
178
231
|
function removeSuckerDeployer(address deployer) external;
|
|
179
232
|
|
|
233
|
+
/// @notice Remove a local-to-remote token mapping from the allowlist.
|
|
234
|
+
/// @param localToken The local token address.
|
|
235
|
+
/// @param remoteChainId The ID of the remote chain.
|
|
236
|
+
/// @param remoteToken The remote token address encoded as bytes32.
|
|
237
|
+
function removeTokenMapping(address localToken, uint256 remoteChainId, bytes32 remoteToken) external;
|
|
238
|
+
|
|
239
|
+
/// @notice Remove multiple local-to-remote token mappings from the allowlist.
|
|
240
|
+
/// @param localTokens The local token addresses.
|
|
241
|
+
/// @param remoteChainIds The remote chain IDs.
|
|
242
|
+
/// @param remoteTokens The remote token addresses encoded as bytes32.
|
|
243
|
+
function removeTokenMappings(
|
|
244
|
+
address[] calldata localTokens,
|
|
245
|
+
uint256[] calldata remoteChainIds,
|
|
246
|
+
bytes32[] calldata remoteTokens
|
|
247
|
+
)
|
|
248
|
+
external;
|
|
249
|
+
|
|
180
250
|
/// @notice Set the ETH fee (in wei) paid on each toRemote() call. Owner only.
|
|
181
251
|
/// @param fee The new fee amount in wei.
|
|
182
252
|
function setToRemoteFee(uint256 fee) external;
|
|
@@ -5,22 +5,24 @@ import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol
|
|
|
5
5
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
6
6
|
import {IJBMultiTerminal} from "@bananapus/core-v6/src/interfaces/IJBMultiTerminal.sol";
|
|
7
7
|
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
8
|
-
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
9
8
|
import {JBRulesetMetadataResolver} from "@bananapus/core-v6/src/libraries/JBRulesetMetadataResolver.sol";
|
|
9
|
+
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
10
10
|
import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
|
|
11
11
|
import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
|
|
12
12
|
import {IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
|
|
13
13
|
|
|
14
14
|
import {IJBPeerChainAdjustedAccounts} from "../interfaces/IJBPeerChainAdjustedAccounts.sol";
|
|
15
15
|
import {IJBSuckerRegistry} from "../interfaces/IJBSuckerRegistry.sol";
|
|
16
|
+
|
|
16
17
|
import {JBPeerChainAdjustedAccountsLib} from "./JBPeerChainAdjustedAccountsLib.sol";
|
|
18
|
+
import {MerkleLib} from "../utils/MerkleLib.sol";
|
|
19
|
+
|
|
17
20
|
import {JBAccountingSnapshot} from "../structs/JBAccountingSnapshot.sol";
|
|
18
21
|
import {JBChainAccounting} from "../structs/JBChainAccounting.sol";
|
|
19
22
|
import {JBInboxTreeRoot} from "../structs/JBInboxTreeRoot.sol";
|
|
20
23
|
import {JBMessageRoot} from "../structs/JBMessageRoot.sol";
|
|
21
24
|
import {JBPeerChainContext} from "../structs/JBPeerChainContext.sol";
|
|
22
25
|
import {JBSourceContext} from "../structs/JBSourceContext.sol";
|
|
23
|
-
import {MerkleLib} from "../utils/MerkleLib.sol";
|
|
24
26
|
|
|
25
27
|
/// @notice Library with bytecode-heavy functions extracted from JBSucker to reduce child contract sizes.
|
|
26
28
|
/// @dev These are `external` library functions, so they are deployed as a separate contract and called via
|
|
@@ -186,23 +188,20 @@ library JBSuckerLib {
|
|
|
186
188
|
}
|
|
187
189
|
}
|
|
188
190
|
|
|
189
|
-
/// @notice Folds a peer chain's
|
|
190
|
-
/// local currency.
|
|
191
|
+
/// @notice Folds a peer chain's source contexts into per-currency surplus and balance.
|
|
191
192
|
/// @dev Extracted from `JBSucker.peerChainContextsOf` to reduce child contract bytecode. Called via DELEGATECALL.
|
|
192
|
-
///
|
|
193
|
-
///
|
|
194
|
-
///
|
|
195
|
-
///
|
|
196
|
-
///
|
|
193
|
+
/// The caller stores mapped remote-token keys in its local token namespace before this runs. This derives each
|
|
194
|
+
/// token's authoritative accounting-context currency and merges entries that share BOTH currency AND decimals. The
|
|
195
|
+
/// accounting-context currency is immutable, so re-resolving on each read is safe. Entries that share a currency
|
|
196
|
+
/// but carry different decimals stay separate, since the raw amounts are on different scales. No price oracle is
|
|
197
|
+
/// consulted.
|
|
197
198
|
/// @param directory The JB directory to look up the project's terminals.
|
|
198
199
|
/// @param projectId The project whose accounting contexts to read.
|
|
199
|
-
/// @param
|
|
200
|
-
/// @param rawContexts The peer chain's raw per-context surplus and balance.
|
|
200
|
+
/// @param rawContexts The peer chain's per-context surplus and balance.
|
|
201
201
|
/// @return contexts The per-currency surplus and balance for the chain.
|
|
202
202
|
function foldPeerContexts(
|
|
203
203
|
IJBDirectory directory,
|
|
204
204
|
uint256 projectId,
|
|
205
|
-
address[] memory localTokens,
|
|
206
205
|
JBSourceContext[] memory rawContexts
|
|
207
206
|
)
|
|
208
207
|
external
|
|
@@ -219,7 +218,8 @@ library JBSuckerLib {
|
|
|
219
218
|
uint8 ctxDecimals = rawContexts[i].decimals;
|
|
220
219
|
uint128 ctxSurplus = rawContexts[i].surplus;
|
|
221
220
|
uint128 ctxBalance = rawContexts[i].balance;
|
|
222
|
-
|
|
221
|
+
address token = address(uint160(uint256(rawContexts[i].token)));
|
|
222
|
+
uint32 ctxCurrency = _currencyOf({directory: directory, projectId: projectId, token: token});
|
|
223
223
|
|
|
224
224
|
// Fold into an existing entry that matches on BOTH currency AND decimals, or append a new one.
|
|
225
225
|
bool merged;
|
|
@@ -265,7 +265,8 @@ library JBSuckerLib {
|
|
|
265
265
|
/// peer records are gathered from the registry, which is the only contract that sees a hub chain's per-peer
|
|
266
266
|
/// suckers together; it dedups them to the freshest per chain. A reverting or unset registry yields a local-only
|
|
267
267
|
/// bundle, so a standalone sucker still propagates its own record. Forwarded peer records keep their own origin
|
|
268
|
-
/// chain and freshness key
|
|
268
|
+
/// chain and freshness key, while their context token keys are already localized by the sucker they were gathered
|
|
269
|
+
/// from so the receiver only needs its mapping to the direct peer's token.
|
|
269
270
|
/// @param directory The JB directory to look up controllers and terminals.
|
|
270
271
|
/// @param registry The sucker registry that aggregates the project's per-chain records.
|
|
271
272
|
/// @param projectId The project to snapshot.
|
|
@@ -297,7 +298,7 @@ library JBSuckerLib {
|
|
|
297
298
|
peers = gathered;
|
|
298
299
|
} catch {}
|
|
299
300
|
|
|
300
|
-
// The local record leads; forwarded peer records follow
|
|
301
|
+
// The local record leads; forwarded peer records follow, keeping their own origin chain and freshness.
|
|
301
302
|
accounts = new JBChainAccounting[](peers.length + 1);
|
|
302
303
|
accounts[0] = JBChainAccounting({
|
|
303
304
|
chainId: block.chainid, totalSupply: localTotalSupply, contexts: localContexts, timestamp: sourceTimestamp
|
|
@@ -4,24 +4,23 @@ pragma solidity ^0.8.0;
|
|
|
4
4
|
import {JBSourceContext} from "./JBSourceContext.sol";
|
|
5
5
|
|
|
6
6
|
/// @notice One source chain's project-wide accounting, carried as a record in a cross-chain gossip bundle.
|
|
7
|
-
/// @dev A sucker sends its own chain's record alongside every peer-chain record it already holds
|
|
8
|
-
///
|
|
9
|
-
/// project's accounting propagates across a hub-and-spoke sucker mesh
|
|
10
|
-
///
|
|
11
|
-
///
|
|
12
|
-
///
|
|
13
|
-
///
|
|
14
|
-
/// project's same-address sucker invariant — the same CREATE2
|
|
15
|
-
/// relies on — and a peer running adversarial bytecode could
|
|
16
|
-
/// gate bounds rollback, not authorship; the supply view it feeds
|
|
17
|
-
/// surplus, so a forged record cannot by itself over-credit a cash out.
|
|
7
|
+
/// @dev A sucker sends its own chain's record alongside every peer-chain record it already holds. Each record is
|
|
8
|
+
/// stamped with its originating chain's freshness key. The receiving chain stores the freshest record per source chain,
|
|
9
|
+
/// so a project's accounting propagates across a hub-and-spoke sucker mesh without a direct sucker between every pair
|
|
10
|
+
/// of chains. `contexts` are un-valued token-denominated amounts. A direct sender's own record carries source-chain
|
|
11
|
+
/// token addresses. Forwarded peer records use the forwarding hub's local same-asset token when a mapping exists, so
|
|
12
|
+
/// the next receiver only needs a mapping to that hub token. Trust is transitive across the mesh: a receiver
|
|
13
|
+
/// authenticates only the directly-bridged peer that delivers a bundle, not the origin of each forwarded record. A
|
|
14
|
+
/// record is therefore only as trustworthy as the project's same-address sucker invariant — the same CREATE2
|
|
15
|
+
/// same-bytecode assumption every paired sucker already relies on — and a peer running adversarial bytecode could
|
|
16
|
+
/// forge another chain's record. The freshest-per-chain gate bounds rollback, not authorship; the supply view it feeds
|
|
17
|
+
/// is clamped downstream by each chain's own local surplus, so a forged record cannot by itself over-credit a cash out.
|
|
18
18
|
/// @custom:member chainId The source chain this record describes. A receiver ignores a record for its own chain, since
|
|
19
19
|
/// it reads its own local accounting directly.
|
|
20
20
|
/// @custom:member totalSupply The total token supply (including reserved tokens) on the source chain when the record
|
|
21
21
|
/// was taken. Used by the receiving chain to track cross-chain supply for cash out tax calculations.
|
|
22
|
-
/// @custom:member contexts The source chain's surplus and balance per accounting context, each in
|
|
23
|
-
///
|
|
24
|
-
/// at par.
|
|
22
|
+
/// @custom:member contexts The source chain's surplus and balance per accounting context, each in its own decimals and
|
|
23
|
+
/// un-valued. The token key may be the source token for direct records or a forwarding hub's local same-asset token.
|
|
25
24
|
/// @custom:member timestamp A monotonic source-chain freshness key for the record. The receiver keeps the freshest
|
|
26
25
|
/// record per source chain, so stale relays cannot roll back surplus, balance, or supply.
|
|
27
26
|
struct JBChainAccounting {
|
|
@@ -3,10 +3,10 @@ pragma solidity ^0.8.0;
|
|
|
3
3
|
|
|
4
4
|
/// @notice A remote chain's surplus and balance for one local currency, as last received.
|
|
5
5
|
/// @dev One entry per local currency in the latest snapshot. The local currency is the project's authoritative
|
|
6
|
-
/// accounting-context currency for the token
|
|
7
|
-
///
|
|
8
|
-
///
|
|
9
|
-
///
|
|
6
|
+
/// accounting-context currency for the stored token key. A receiver stores mapped remote-token keys under its local
|
|
7
|
+
/// same-asset token before this view is built. `surplus`/`balance` are raw, un-valued amounts in that currency's own
|
|
8
|
+
/// units; a read values them into a requested currency via the prices contract, exactly as the terminal store values
|
|
9
|
+
/// local surplus (an identity short-circuit means same-currency reads are taken at par).
|
|
10
10
|
/// @custom:member currency The local currency this context resolves to.
|
|
11
11
|
/// @custom:member decimals The context's native decimal precision, used to rescale to a requested precision.
|
|
12
12
|
/// @custom:member surplus The raw surplus held in this currency on the peer chain.
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
|
-
/// @notice One source-chain accounting context, carried
|
|
4
|
+
/// @notice One source-chain accounting context, carried un-valued in a cross-chain snapshot.
|
|
5
5
|
/// @dev The source chain reports its surplus and balance per accounting context in that context's own decimals — it
|
|
6
|
-
/// performs no price-feed valuation.
|
|
7
|
-
///
|
|
8
|
-
///
|
|
9
|
-
///
|
|
10
|
-
///
|
|
11
|
-
///
|
|
12
|
-
/// surplus.
|
|
13
|
-
/// @custom:member token The source-local token this context was read from, resolved to a local token (and thence a
|
|
14
|
-
/// local currency) on receipt. Padded to bytes32 for cross-VM compatibility.
|
|
6
|
+
/// performs no price-feed valuation. A receiver stores `token` as its own local token when a remote-token mapping
|
|
7
|
+
/// exists, or leaves the token key unchanged otherwise. Read paths derive the context's currency from that stored token
|
|
8
|
+
/// key instead of trusting a wire-carried currency, so same-asset tokens at different addresses (e.g. USDC) still fold
|
|
9
|
+
/// under the receiver's own currency. Any later valuation uses the project's normal local surplus conversions.
|
|
10
|
+
/// @custom:member token The source token key, or a receiver-local token key after a remote-token mapping has been
|
|
11
|
+
/// applied. Padded to bytes32 for cross-VM compatibility.
|
|
15
12
|
/// @custom:member decimals The context's native decimal precision (e.g. 18 for ETH, 6 for USDC).
|
|
16
13
|
/// @custom:member surplus The raw, un-valued surplus held in this context, in the context's own units. Capped to
|
|
17
14
|
/// `uint128` for cross-VM (SVM) compatibility, matching the leaf-amount cap.
|