@bananapus/suckers-v6 1.0.0 → 1.0.1

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/suckers-v6",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
package/src/JBSucker.sol CHANGED
@@ -332,12 +332,12 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
332
332
  /// project's chain count.
333
333
  uint256[] private _peerChainIds;
334
334
 
335
- /// @notice Each peer chain's raw, un-valued per-context surplus and balance from its latest accepted record.
336
- /// @dev Stored exactly as received — in the source chain's own token addresses and decimals so the record can
337
- /// be
338
- /// re-gossiped to other chains faithfully and resolved to local currencies at read time. A fresher record for a
339
- /// chain replaces that chain's set from scratch; a context dropped by the fresher record simply vanishes.
340
- /// @custom:param chainId The peer chain to read the raw contexts of.
335
+ /// @notice Each peer chain's un-valued per-context surplus and balance from its latest accepted record.
336
+ /// @dev Stored in this sucker's token namespace where a remote-token mapping exists; unmapped tokens stay as
337
+ /// received. `peerChainContextsOf` resolves these token keys for local valuation.
338
+ /// A fresher record for a chain replaces that chain's set from scratch; a context dropped by the fresher record
339
+ /// simply vanishes.
340
+ /// @custom:param chainId The peer chain to read the contexts of.
341
341
  mapping(uint256 chainId => JBSourceContext[]) private _peerContextsOf;
342
342
 
343
343
  //*********************************************************************//
@@ -882,11 +882,11 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
882
882
  return _outboxOf[token];
883
883
  }
884
884
 
885
- /// @notice The raw, un-valued accounting record this sucker holds for every peer chain it has heard about.
886
- /// @dev The registry reads this to gather a project's full cross-chain knowledge and re-gossip it. Records are
887
- /// returned exactly as received (in each source chain's own token addresses and decimals) so the next receiver
888
- /// resolves them to its own local currencies independently.
889
- /// @return accounts One raw accounting record per known peer chain.
885
+ /// @notice The un-valued accounting record this sucker holds for every peer chain it has heard about.
886
+ /// @dev The registry reads this to gather a project's full cross-chain knowledge and re-gossip it. Incoming
887
+ /// contexts are stored in this sucker's token namespace where a mapping exists, so a hub forwards sibling-chain
888
+ /// accounting in hub-local token terms and the next spoke only needs its mapping to the hub token.
889
+ /// @return accounts One accounting record per known peer chain.
890
890
  function peerChainAccountsOf() external view returns (JBChainAccounting[] memory accounts) {
891
891
  uint256[] storage chainIds = _peerChainIds;
892
892
  uint256 numChains = chainIds.length;
@@ -907,10 +907,10 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
907
907
 
908
908
  /// @notice One peer chain's per-currency surplus and balance from its latest accepted record, plus that record's
909
909
  /// freshness key.
910
- /// @dev Resolves each raw stored context to its local currency and folds same-currency, same-decimals entries
911
- /// together. The result is un-valued — the registry values each context into a requested currency; the sucker
912
- /// consults no price oracle. Contexts that share a currency but carry different decimals are kept separate because
913
- /// the raw amounts are on different scales and cannot be summed directly.
910
+ /// @dev Stored contexts are already keyed in this sucker's token namespace where a mapping exists. This folds
911
+ /// same-currency, same-decimals entries together. The result is un-valued — the registry values each context into
912
+ /// a requested currency; the sucker consults no price oracle. Contexts that share a currency but carry different
913
+ /// decimals are kept separate because the raw amounts are on different scales and cannot be summed directly.
914
914
  /// @param chainId The peer chain to read the contexts of.
915
915
  /// @return contexts The per-currency surplus and balance for the chain.
916
916
  /// @return snapshot The source freshness key of the chain's latest accepted record.
@@ -919,25 +919,8 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
919
919
  view
920
920
  returns (JBPeerChainContext[] memory contexts, uint256 snapshot)
921
921
  {
922
- JBSourceContext[] storage rawContexts = _peerContextsOf[chainId];
923
- uint256 numRaw = rawContexts.length;
924
-
925
- // Copy the raw contexts to memory and resolve each source-local token to a local token (mapping first, identity
926
- // fallback). The bytecode-heavy currency resolution and fold then run in the library.
927
- JBSourceContext[] memory raw = new JBSourceContext[](numRaw);
928
- address[] memory localTokens = new address[](numRaw);
929
- for (uint256 i; i < numRaw;) {
930
- JBSourceContext storage ctx = rawContexts[i];
931
- raw[i] = ctx;
932
- address contextToken = _localTokenForRemoteToken[ctx.token];
933
- localTokens[i] = contextToken == address(0) ? _toAddress(ctx.token) : contextToken;
934
- unchecked {
935
- ++i;
936
- }
937
- }
938
-
939
922
  contexts = JBSuckerLib.foldPeerContexts({
940
- directory: DIRECTORY, projectId: projectId(), localTokens: localTokens, rawContexts: raw
923
+ directory: DIRECTORY, projectId: projectId(), rawContexts: _peerContextsOf[chainId]
941
924
  });
942
925
  snapshot = snapshotTimestampOf[chainId];
943
926
  }
@@ -1600,12 +1583,13 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
1600
1583
  /// @notice Store one source chain's accounting record if it is fresher than the one already held for that chain.
1601
1584
  /// @dev A record for the local chain is ignored — a chain reads its own accounting directly. Each peer chain is
1602
1585
  /// gated independently on a strictly-newer freshness key, so a stale relay cannot roll back any chain and records
1603
- /// delivered out of order converge. Contexts are stored raw (in the source chain's own token addresses) so the
1604
- /// record can be re-gossiped faithfully; each receiver resolves them to its own local currencies at read time.
1586
+ /// delivered out of order converge. Any source token that matches this sucker's remote-token mapping is re-keyed
1587
+ /// to this sucker's local token before storage, so forwarded records are already in the current hop's token
1588
+ /// namespace.
1605
1589
  /// @param chainId The source chain the record describes.
1606
1590
  /// @param sourceTimestamp The record's source-chain freshness key.
1607
1591
  /// @param sourceTotalSupply The source chain's total project-token supply.
1608
- /// @param sourceContexts The source chain's raw per-context surplus and balance.
1592
+ /// @param sourceContexts The source chain's per-context surplus and balance.
1609
1593
  function _storeChainAccounting(
1610
1594
  uint256 chainId,
1611
1595
  uint256 sourceTimestamp,
@@ -1634,15 +1618,17 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
1634
1618
  _peerChainIds.push(chainId);
1635
1619
  }
1636
1620
 
1637
- // Rebuild this chain's raw context set from scratch. A context dropped by the fresher record is simply absent.
1638
- // Each context is stored verbatim so it can be re-gossiped faithfully; folding to a local currency happens at
1639
- // read time in `peerChainContextsOf`.
1621
+ // Rebuild this chain's context set from scratch. A context dropped by the fresher record is simply absent.
1622
+ // Each context keeps its source amount and decimals, while its token key is localized for this hop when a
1623
+ // mapping exists.
1640
1624
  delete _peerContextsOf[chainId];
1641
1625
  JBSourceContext[] storage storedContexts = _peerContextsOf[chainId];
1642
1626
 
1643
1627
  uint256 numContexts = sourceContexts.length;
1644
1628
  for (uint256 i; i < numContexts;) {
1645
1629
  storedContexts.push(sourceContexts[i]);
1630
+ address localToken = _localTokenForRemoteToken[sourceContexts[i].token];
1631
+ if (localToken != address(0)) storedContexts[i].token = bytes32(uint256(uint160(localToken)));
1646
1632
  unchecked {
1647
1633
  ++i;
1648
1634
  }
@@ -610,7 +610,7 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
610
610
  return ERC2771Context._contextSuffixLength();
611
611
  }
612
612
 
613
- /// @notice Reads one sucker's raw records and folds each into the per-chain gather scratch.
613
+ /// @notice Reads one sucker's locally-keyed records and folds each into the per-chain gather scratch.
614
614
  /// @dev Extracted from `peerChainAccountsOf` to keep its stack shallow. A sucker that reverts contributes nothing.
615
615
  /// The destination chain, the local chain, and chain 0 are excluded.
616
616
  /// @param scratch The per-chain gather scratch to fold records into.
@@ -706,8 +706,8 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
706
706
  scratch.hasActiveValue = new bool[](len);
707
707
  }
708
708
 
709
- /// @notice Records one source chain's raw accounting record into a per-chain gather scratch, keeping the freshest.
710
- /// @dev Mirrors `_recordPeerValue`'s selection rule for raw records: an active sucker's record supersedes a
709
+ /// @notice Records one source chain's accounting record into a per-chain gather scratch, keeping the freshest.
710
+ /// @dev Mirrors `_recordPeerValue`'s selection rule for gathered records: an active sucker's record supersedes a
711
711
  /// deprecated one's for the same chain; among same-state records the strictly-fresher timestamp wins; equal
712
712
  /// freshness keeps the first writer, since records from one origin chain at one freshness key are identical. Used
713
713
  /// to gather records for re-gossiping.
@@ -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 raw, un-valued accounting record this sucker holds for every peer chain it has heard about.
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
- /// returned exactly as received so the next receiver resolves them to its own local currencies independently.
164
- /// @return accounts One raw accounting record per known peer chain.
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
@@ -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 raw source contexts into per-currency surplus and balance, resolving each to a
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
- /// Each `localTokens[i]` is the local token the sucker resolved `rawContexts[i].token` to (via its token mapping or
193
- /// identity); this derives that token's authoritative accounting-context currency and merges entries that share
194
- /// BOTH currency AND decimals. The accounting-context currency is immutable, so re-resolving on each read is safe.
195
- /// Entries that share a currency but carry different decimals stay separate, since the raw amounts are on different
196
- /// scales. No price oracle is consulted.
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 localTokens The local token each raw context resolves to, parallel to `rawContexts`.
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
- uint32 ctxCurrency = _currencyOf({directory: directory, projectId: projectId, token: localTokens[i]});
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 so the receiver gates each chain independently.
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 verbatim, keeping their own origin chain and freshness.
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, each stamped with
8
- /// the originating chain's own freshness key. The receiving chain stores the freshest record per source chain, so a
9
- /// project's accounting propagates across a hub-and-spoke sucker mesh (L2s bridged only through mainnet) without a
10
- /// direct sucker between every pair of chains. `contexts` carry the source chain's own token addresses, so each
11
- /// receiver resolves them to its own local currencies independently. Trust is transitive across the mesh: a receiver
12
- /// authenticates only the directly-bridged peer that delivers a bundle, not the origin of each forwarded record, so
13
- /// any authenticated peer can forward a record for any other chain. A record is therefore only as trustworthy as the
14
- /// project's same-address sucker invariant — the same CREATE2 same-bytecode assumption every paired sucker already
15
- /// relies on — and a peer running adversarial bytecode could forge another chain's record. The freshest-per-chain
16
- /// gate bounds rollback, not authorship; the supply view it feeds is clamped downstream by each chain's own local
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 the context's own
23
- /// currency and decimals, un-valued. The receiver resolves each entry to its same-asset local context and folds it in
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 a remote context resolves to (identity for same-address tokens, or via
7
- /// the sucker's token mapping for same-asset tokens at different addresses). `surplus`/`balance` are raw, un-valued
8
- /// amounts in that currency's own units; a read values them into a requested currency via the prices contract, exactly
9
- /// as the terminal store values local surplus (an identity short-circuit means same-currency reads are taken at par).
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 raw (un-valued) in a cross-chain snapshot.
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. The destination chain resolves `token` to its own local token (identity for
7
- /// same-address tokens, or via the sucker's remote-to-local token mapping for same-asset tokens at different
8
- /// addresses), derives that local token's currency as `uint32(uint160(localToken))`, and folds the context into its
9
- /// same-currency local context at par. The destination derives the currency from the resolved local token rather than
10
- /// trusting a wire-carried currency, so a same-asset token at a different address (e.g. USDC) still folds under the
11
- /// receiver's own currency. The only conversions ever performed are the ones a project already needs for its own local
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.