Skip to content

Refactor: Spoke => Spoke, SpokeHandler and SpokeRegistry#778

Closed
lemunozm wants to merge 15 commits intomainfrom
ref/spoke-refactor
Closed

Refactor: Spoke => Spoke, SpokeHandler and SpokeRegistry#778
lemunozm wants to merge 15 commits intomainfrom
ref/spoke-refactor

Conversation

@lemunozm
Copy link
Copy Markdown
Contributor

@lemunozm lemunozm commented Feb 16, 2026

Description

  • Makes Spoke to follow the same pattern as Hub:
    • Spoke => High level with no state methods for user interaction
    • SpokeHandler => Methods comming from Hub though messages
    • SpokeRegistry => State easily mutated using simple functions

Spoke diagram

image

A future vaults implementation will use mostly both:

  • Spoke: to perform requests
    • NOTE: SpokeRegistry can be retriever from Spoke
  • VaultRegistry to look for the registered vault

Ideally processing a message should not call directly to VaultRegistry, instead should call to SpokeHandler, which would call VaultRegistry. I mean both SpokeRegistry and Vault registry should be at the same level handled by SpokeHandler. I think I can have this without migrating VaultRegistry just by coping VaultRegisty.updateVault() method into SpokeHandler. WDYT cc @hieronx?

## Messaging diagram

image

@lemunozm lemunozm self-assigned this Feb 16, 2026
Comment thread src/vaults/AsyncRequestManager.sol Outdated
using RequestCallbackMessageLib for *;

ISpoke public spoke;
ISpokeRegistry public spokeRegistry;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also do spoke.spokeRegistry() to remove this extra dependency, but should save more gas if we have both

@wischli wischli added 💡 idea breaking Breaking change to existing interfaces or behavior type:refactor Code restructure, no behavior change type:idea Discussion / proposal and removed 🚨 breaking change labels Apr 8, 2026
@wischli wischli added audit:needed Requires audit review scope:contracts Modifies deployed or deployable contract code version:v3.3.0 Scoped for v3.3.0 and removed v3.3.0 labels Apr 8, 2026

/// @inheritdoc IBalanceSheet
function file(bytes32 what, address data) external auth {
if (what == "spoke") spoke = ISpoke(data);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for myself: needs to be called "spoke" to have the same bytecode as v3.1.0

/// @inheritdoc IVaultRegistry
function file(bytes32 what, address data) external auth {
if (what == "spoke") spoke = ISpoke(data);
if (what == "spokeRegistry") spokeRegistry = ISpokeRegistry(data);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applies if we remove VaultRegistry

/// @inheritdoc IMerkleProofManagerFactory
function newManager(PoolId poolId) external returns (IMerkleProofManager) {
require(balanceSheet.spoke().isPoolActive(poolId), InvalidPoolId());
require(balanceSheet.spokeRegistry().isPoolActive(poolId), InvalidPoolId());
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be called spoke() but maybe not need it as this contract will be also deprecated

function spoke() external view returns (ISpoke);
/// @notice Returns the spoke registry contract
/// @return The spoke registry contract instance
function spokeRegistry() external view returns (ISpokeRegistry);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be called spoke() to avoid any changes here

@github-actions
Copy link
Copy Markdown

Coverage after merging ref/spoke-refactor into main will be

96.94%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/adapters
   AxelarAdapter.sol97.67%90%100%100%118
   ChainlinkAdapter.sol98.11%91.67%100%100%95
   LayerZeroAdapter.sol98.41%90%100%100%124
   RecoveryAdapter.sol100%100%100%100%
   WormholeAdapter.sol97.67%90%100%100%108
src/admin
   OpsGuardian.sol100%100%100%100%
   ProtocolGuardian.sol100%100%100%100%
   Root.sol100%100%100%100%
   TokenRecoverer.sol100%100%100%100%
src/core/hub
   Accounting.sol97.92%96%100%98.31%134, 137
   Holdings.sol97.71%88.89%100%100%118, 243, 82
   Hub.sol93.98%81.13%93.02%97.54%269, 287, 305, 339, 343, 374–375, 413, 498–499, 544, 549, 586, 601, 87
   HubHandler.sol98%90.91%100%100%71
   HubRegistry.sol92.39%76.67%100%100%118, 124, 130, 35, 46, 79, 99
   ShareClassManager.sol98.81%95.45%100%100%42
src/core/libraries
   PricingLib.sol100%100%100%100%
src/core/messaging
   GasService.sol96.55%100%87.50%96.20%106, 135, 145
   Gateway.sol100%100%100%100%
   MessageDispatcher.sol99.60%98.55%100%100%733
   MessageProcessor.sol82.42%60.26%100%99.01%101, 103, 109, 112, 114, 125, 130, 139, 144, 147, 159, 162, 165, 170, 180, 185, 194, 197, 200, 213, 224, 229, 232, 236, 83–84, 87–88, 91–92, 97, 99
   MultiAdapter.sol100%100%100%100%
src/core/messaging/libraries
   MessageLib.sol100%100%100%100%
src/core/spoke
   BalanceSheet.sol97.47%96.97%92.59%98.55%291, 348, 57
   PoolEscrow.sol100%100%100%100%
   ShareToken.sol92.41%60%94.44%98.04%100, 112, 144, 146, 32
   Spoke.sol88.89%62.50%100%100%101, 70, 70–71, 71, 98, 98–99, 99
   SpokeHandler.sol100%100%100%100%
   SpokeRegistry.sol100%100%100%100%
   VaultRegistry.sol92.86%82.86%100%98.18%130, 54, 60–62, 98–99
src/core/spoke/factories
   PoolEscrowFactory.sol100%100%100%100%
   TokenFactory.sol100%100%100%100%
src/core/spoke/legacy
   SpokeV3_1_0.sol67.92%50%69.23%71.88%135–136, 37–39, 58–59, 74–75, 79–80, 84–85
src/core/utils
   BatchedMulticall.sol100%100%100%100%
   ContractUpdater.sol100%100%100%100%
src/deployment
   ActionBatchers.sol85.44%62.50%80%87.08%460, 462–463, 465, 465–466, 468, 470, 470–471, 475, 475–476, 478, 481, 481–482, 484, 487, 487–488, 491, 494, 494, 496, 498, 530–534, 539–540, 542–543, 545–546
src/hooks
   BaseTransferHook.sol100%100%100%100%
   FreelyTransferable.sol92.31%80%100%100%37
   FreezeOnly.sol100%100%100%100%
   FullRestrictions.sol90.48%77.78%100%100%46–47
   RedemptionRestrictions.sol85.71%50%100%100%37
src/hooks/libraries
   UpdateRestrictionMessageLib.sol90%50%100%100%40, 61, 82
src/managers/hub
   NAVManager.sol100%100%100%100%
   SimplePriceManager.sol100%100%100%100%
src/managers/spoke
   MerkleProofManager.sol97.01%87.50%100%100%103, 110
   OnOfframpManager.sol100%100%100%100%
   QueueManager.sol100%100%100%100%
src/managers/spoke/decoders
   BaseDecoder.sol100%100%100%100%
   CircleDecoder.sol83.33%100%100%75%22
   VaultDecoder.sol100%100%100%100%
src/misc
   Auth.sol100%100%100%100%
   ERC20.sol100%100%100%100%
   Escrow.sol56.25%33.33%100%66.67%17, 19, 23–24, 24, 24, 26
   Multicall.sol91.67%66.67%100%100%19
   Recoverable.sol100%100%100%100%
   ReentrancyProtection.sol90%75%100%100%24
src/misc/libraries
   ArrayLib.sol100%100%100%100%
   BitmapLib.sol100%100%100%100%
   BytesLib.sol90.27%56%100%100%109, 120, 131, 14, 142, 153, 16, 164, 175, 186, 87
   CastLib.sol95.24%66.67%100%100%10, 34
   EIP712Lib.sol100%100%100%100%
   ExcessivelySafeCallLib.sol100%100%100%100%
   MathLib.sol93.46%76.19%100%97.33%34–35, 44, 46, 48, 50, 52
   MerkleProofLib.sol100%100%100%100%
   SafeTransferLib.sol96.97%92.86%100%100%75
   SignatureLib.sol95.24%80%100%100%17
   StringLib.sol100%100%100%100%
   TransientArrayLib.sol100%100%100%100%
   TransientBytesLib.sol100%100%100%100%
   TransientStorageLib.sol100%100%100%100%
src/spell
   V2CleaningsSpell.sol0%0%0%0%100–103, 103–106, 111–112, 112–114, 114, 114–116, 116, 116–118, 118, 118–119, 121, 124, 126–127, 129, 133–137, 46–47, 47, 47–48, 50–53, 55, 55–56, 58, 61, 63, 63, 65, 65–66, 66–69, 71, 75, 75–76, 81, 81–82, 82–84, 86–87, 91–94, 96–97
src/utils
   RefundEscrow.sol100%100%100%100%
   RefundEscrowFactory.sol100%100%100%100%
   SubsidyManager.sol100%100%100%100%
src/valuations
   IdentityValuation.sol100%100%100%100%
   OracleValuation.sol100%100%100%100%
src/vaults
   AsyncRequestManager.sol96.85%86.36%100%99.64%195, 198, 201, 204, 215, 227, 295, 328, 455, 460, 505, 573, 580
   AsyncVault.sol96.25%83.33%95%98.15%146, 47
   BaseVaults.sol93.50%80.77%95.24%95.45%124, 137, 239, 309–310, 85–86, 86, 86–88
   BatchRequestManager.sol100%100%100%100%
   SyncDepositVault.sol100%100%100%100%
   SyncManager.sol98.58%92.59%100%100%67, 72
   VaultRouter.sol87.50%58.82%100%91.67%113, 113, 115–116, 116, 118, 155, 76, 79–80, 92–93
src/vaults/factories
   AsyncVaultFactory.sol93.75%50%100%100%32
   SyncDepositVaultFactory.sol95%50%100%100%40
src/vaults/libraries
   RequestCallbackMessageLib.sol89.58%50%100%100%104, 139, 38, 57, 77
   RequestMessageLib.sol89.74%50%100%100%37, 55, 72, 89

@lemunozm
Copy link
Copy Markdown
Contributor Author

Closed in favor to an internal mirror: https://github.com/centrifuge/protocol-internal/pull/174

@lemunozm
Copy link
Copy Markdown
Contributor Author

@lemunozm lemunozm closed this Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

audit:needed Requires audit review breaking Breaking change to existing interfaces or behavior scope:contracts Modifies deployed or deployable contract code type:idea Discussion / proposal type:refactor Code restructure, no behavior change version:v3.3.0 Scoped for v3.3.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants