Skip to content

Refactor ChatterPayWalletFactory to Support Config Updates #175

@dappsar

Description

@dappsar

📋 Context

The ChatterPayWalletFactory contract is responsible for deploying and managing wallet proxies. However, several critical parameters are currently hardcoded during deployment and cannot be updated afterward:

  • entryPoint: Set in constructor, immutable.
  • paymaster: Set in constructor, stored as a mutable variable but lacks a setter.
  • router: Set in constructor, immutable.

This creates several limitations:

  • 🔒 Inflexibility: If a new version of Paymaster, Router, or EntryPoint is deployed, the factory cannot redirect newly created wallets to those updated addresses.
  • 🧱 Code Coupling: Wallets created after upgrades require redeploying a new factory with new parameters.
  • Operational Overhead: Each factory redeployment causes fragmentation in wallet management and configuration, increasing gas and deployment complexity.

🛠 Strategy

Several strategies were considered to address this:

Strategy Description Pros Cons
A. Add setters in the factory Add setEntryPoint(), setPaymaster(), setRouter() functions Simple, maintains single factory Risk of improper updates
B. Move to upgradeable factory Make the factory upgradeable (UUPS proxy) Full control Adds complexity, audit risk
C. Use registry pattern Central config registry for contracts to query Highly modular Increased gas cost per wallet creation

The recommended approach is Strategy A.


🔬 Gas Analysis

The current gas cost of setting these fields during construction is negligible.

After adding setters:

  • SSTORE: ~20,000 gas per update (executed only once per change).
  • No additional runtime cost unless createProxy() is updated to query these variables dynamically.

✅ Recommended Implementation

Implement individual setters for each configurable field:

function setPaymaster(address _paymaster) external onlyOwner {
    if (_paymaster == address(0)) revert ChatterPayWalletFactory__ZeroAddress();
    paymaster = _paymaster;
}

function setRouter(address _router) external onlyOwner {
    if (_router == address(0)) revert ChatterPayWalletFactory__ZeroAddress();
    // optional: emit event
    assembly { sstore(router.slot, _router) }
}

function setEntryPoint(address _entryPoint) external onlyOwner {
    if (_entryPoint == address(0)) revert ChatterPayWalletFactory__ZeroAddress();
    assembly { sstore(entryPoint.slot, _entryPoint) }
}

Note: Since entryPoint and router are currently declared as immutable, they must be changed to public mutable variables in order to support this.

address public entryPoint;
address public router;

📌 Auditor Notes

  • 🔐 Restrict all setters with onlyOwner
  • 📣 Emit events on configuration changes to support off-chain indexers
  • 🧪 Add tests to ensure wallets created after updates use the new configuration
  • 🪵 Consider logging configVersion or similar tracking mechanism

🧠 Final Thoughts

Allowing the factory to update paymaster, router, and entryPoint enables operational agility without compromising on safety (when gated by onlyOwner). This makes onboarding to new infra components easier and keeps wallet creation logic centralized.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request
No fields configured for Feature.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions