Skip to content

feat: world chain account implementation#656

Open
0xOsiris wants to merge 18 commits into
mainfrom
contracts/world-chain-account-router
Open

feat: world chain account implementation#656
0xOsiris wants to merge 18 commits into
mainfrom
contracts/world-chain-account-router

Conversation

@0xOsiris
Copy link
Copy Markdown
Contributor

@0xOsiris 0xOsiris commented May 19, 2026

Note

High Risk
High risk because it introduces new core account-router contracts that rely on DELEGATECALL for signature/policy validation and manage verifier installation/storage invariants, which are security-critical paths.

Overview
Implements the WIP-1001 account router as a new WorldChainAccount contract deployed behind a global beacon (WorldChainAccountUpgradeableBeacon) and reached via WorldChainAccountBeaconProxy.

The router now enforces manager-only installation of an immutable admin verifier and a rotating session key ring (ERC-7201 namespaced storage), and dispatches EIP-1271 signature checks plus session-policy evaluation to installed verifiers via controlled DELEGATECALL (rejecting unknown verifiers before any verifier code runs).

Adds Dispatch fallback handling with an explicit UnknownSelector error, defines IWorldChainAccount + IWorldChainAccountRouterErrors, and updates wip-1001.md references from IWorldChainAccountRouter to IWorldChainAccount.

Reviewed by Cursor Bugbot for commit 8da0f22. Configure here.

@0xOsiris
Copy link
Copy Markdown
Contributor Author

@cursoragent review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 19, 2026

You need to increase your spend limit or enable usage-based billing to run background agents. Go to Cursor

@0xOsiris
Copy link
Copy Markdown
Contributor Author

@cursor review

@0xOsiris
Copy link
Copy Markdown
Contributor Author

0xOsiris commented May 19, 2026

/otto review with GPT 5.5 Max Thinking

@agentotto
Copy link
Copy Markdown
Contributor

agentotto Bot commented May 19, 2026

Let me fetch the PR details first.
Spawning a GPT-5.5 subagent with workspace to do a thorough review.
I've spawned a GPT-5.5 subagent (with high reasoning) to do the review. It will:

  1. Clone the repo and checkout the contracts/world-chain-account-router branch
  2. Thoroughly analyze all changed files with a security focus (DELEGATECALL safety, ERC-7201 storage, EIP-1271, beacon proxy upgrades, access control, session key rotation, etc.)
  3. Post inline comments at specific file/line locations
  4. Post a brief top-level summary with the overall verdict

I'll let you know when it's done! 🔍

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 8da0f22. Configure here.

Copy link
Copy Markdown
Contributor

@agentotto agentotto Bot left a comment

Choose a reason for hiding this comment

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

Security review inline findings.


/// @inheritdoc IWorldChainAccount
function isValidSignatureForAdmin(bytes32 hash, bytes calldata signature)
external
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Critical: these validation dispatch functions perform DELEGATECALL but are callable by any EOA/contract. The restricted-frame invariant only holds when the manager uses STATICCALL; a direct CALL to this function executes the installed verifier in the account's storage with normal state-mutating EVM semantics, so verifier code can SSTORE account validation state, emit logs, make external calls/reenter, etc. This violates the interface's no-non-manager mutation boundary and makes verifier bugs exploitable by anyone. Please gate isValidSignatureForAdmin, isValidSignatureForVerifier, and evaluateSessionPolicyForVerifier with onlyManager (or equivalent) before any delegatecall; the manager can still STATICCALL them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1 I think this comment makes sense. Those fns should be only callable by the world chain account manager, so we could add a modifier that restricts access to it

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.

This is not the case

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why? From the spec it seems these fns are only callable by world chain account manager. If we don't want this, I would specify in the wip better

/// and running each verifier's installation hook. The hash rotation atomically retires
/// every entry from the prior key ring without an explicit per-element wipe.
function _installKeyRing(WorldChainAccountVerifier[] calldata sessionVerifiers) internal {
IWorldChainAccount.KeyRingStorage storage $ = _keyRingStorage();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

High: _installKeyRing does not enforce the WIP key-ring invariants at the router boundary (length in 1..MAX_SESSION_VERIFIERS, non-zero deployed verifier addresses, and no duplicate verifier addresses) before committing keyRingHash/running hooks. Duplicates are especially problematic because keyRingHash commits to multiple descriptors while sessionKeyRing is keyed only by address; the hook runs multiple times and final verifier storage may correspond to only the last descriptor. Relying only on manager validation leaves a manager/predeploy upgrade bug able to install a ring the router cannot faithfully represent. Please validate the full set first, then set keyRingHash and run hooks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1 this comment makes sense

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

as mentioned on slack, we should revisit this!

/// @custom:security-contact security@toolsforhumanity.com
contract WorldChainAccountUpgradeableBeacon is UpgradeableBeacon {
constructor(address implementation_, address initialOwner) UpgradeableBeacon(implementation_, initialOwner) {}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

High: a global UpgradeableBeacon lets the owner change account logic, the immutable MANAGER, and storage assumptions for every account without changing each account's runtime code hash. WIP domains/address derivation and txpool cache invalidation bind WORLD_CHAIN_ACCOUNT_ROUTER_CODE_HASH/keyRingHash, not the beacon implementation hash/version, so an upgrade can silently change validation semantics for already-created accounts and pending signatures. If upgradeability remains, the spec/code should bind or gate implementation versions (e.g. fork/timelock governance plus storage-layout checks and golden tests); otherwise the router implementation should be immutable.

contract WorldChainAccountBeaconProxy is BeaconProxy {
/// @notice The single global `WorldChainAccountUpgradeableBeacon` predeploy.
/// TODO: FIXME(Osiris): Update Spec
address internal constant WORLD_CHAIN_ACCOUNT_BEACON = 0x000000000000000000000000000000000000B0cC;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Spec mismatch: this hard-coded beacon predeploy is not currently an activation parameter in WIP-1001, while the proxy runtime/code hash depends on the immutable beacon address. If clients/managers derive WORLD_CHAIN_ACCOUNT_ROUTER_CODE_HASH with a different beacon address, accounts can satisfy the expected proxy code shape while pointing at the wrong or nonexistent beacon and all calls brick. Please finalize the beacon address in the spec/activation params and add code-hash golden tests.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1, this needs to be added to the wip

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.

/// TODO: FIXME(Osiris): Update Spec

Comment thread pkg/contracts/src/abstract/KeyRingStore.sol Outdated
Comment thread pkg/contracts/src/WorldChainAccount.sol
@agentotto
Copy link
Copy Markdown
Contributor

agentotto Bot commented May 19, 2026

Overall verdict: changes requested before merge. The implementation is compact and broadly follows the WIP-1001 shape, but this is a consensus/account-auth boundary and the inline blockers should be resolved with adversarial tests before landing.

I verified the documented ERC-7201 slot derivation and compiled the changed contracts with solc-js 0.8.28 in this workspace. I could not run the Foundry suite here because forge is not installed in the workspace image, so please rely on CI/full local Foundry runs for final validation.

@agentotto
Copy link
Copy Markdown
Contributor

agentotto Bot commented May 19, 2026

The GPT-5.5 subagent has completed the review. Here's a summary of what was done:

Review complete on PR #656 — "feat: world chain account implementation"

The subagent:

  • Cloned the repo, checked out contracts/world-chain-account-router, and read all 8 changed files
  • Verified the ERC-7201 slot derivation matches the documented formula
  • Posted 6 inline security review comments on specific file+line locations
  • Posted a top-level summary comment

Links:

Note: Foundry tests could not be run because forge isn't installed in the workspace image — manual or CI test verification is recommended before merge given the high-risk nature of this PR.

Copy link
Copy Markdown
Contributor

@alessandromazza98 alessandromazza98 left a comment

Choose a reason for hiding this comment

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

There are some comments that make sense to be addressed.

Requesting changes to point those out, once they're addressed, it's good to be merged

contract WorldChainAccountBeaconProxy is BeaconProxy {
/// @notice The single global `WorldChainAccountUpgradeableBeacon` predeploy.
/// TODO: FIXME(Osiris): Update Spec
address internal constant WORLD_CHAIN_ACCOUNT_BEACON = 0x000000000000000000000000000000000000B0cC;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1, this needs to be added to the wip

/// and running each verifier's installation hook. The hash rotation atomically retires
/// every entry from the prior key ring without an explicit per-element wipe.
function _installKeyRing(WorldChainAccountVerifier[] calldata sessionVerifiers) internal {
IWorldChainAccount.KeyRingStorage storage $ = _keyRingStorage();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1 this comment makes sense

Comment thread pkg/contracts/src/WorldChainAccount.sol
Comment thread pkg/contracts/src/WorldChainAccount.sol
IWorldChainSessionVerifier.ExecutionTraceContext calldata context
) external override sessionInstalled(verifier) returns (bool allowed) {
bytes memory ret =
verifier.functionDelegateCall(abi.encodeCall(IWorldChainSessionVerifier.evaluateSessionPolicy, (context)));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same here


/// @inheritdoc IWorldChainAccount
function isValidSignatureForAdmin(bytes32 hash, bytes calldata signature)
external
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1 I think this comment makes sense. Those fns should be only callable by the world chain account manager, so we could add a modifier that restricts access to it

Copy link
Copy Markdown
Contributor

@alessandromazza98 alessandromazza98 left a comment

Choose a reason for hiding this comment

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

I'm actually approving because I don't want to block you, but please take a look at those comments and address them before merging

struct KeyRingStorage {
address adminVerifier;
bytes32 keyRingHash;
mapping(address verifier => bytes32 keyRingHash) sessionKeyRing;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

not unique in current design.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i still think this struct generally is very elegant implementation tho 👍

/// caller other than `WORLD_CHAIN_ACCOUNT_MANAGER`. Replaces any previously installed
/// key ring on `setKeyRing`.
/// @param sessionVerifiers The ordered session verifier set to install.
function installKeyRing(WorldChainAccountVerifier[] calldata sessionVerifiers) external;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

super nit, but it probably should be Keyring not KeyRing

/// verifier writes any validation-affecting state into its own deterministic account
/// storage namespace. Uses OpenZeppelin's `Address.functionDelegateCall`, which reverts
/// with the callee's returndata on failure.
function _runInstallHook(address verifier, bytes calldata installation) private {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

would it make sense to add a function to the session verifier namespace which defines the storage slot (erc7201) of that verifier (e.g based on a name) and double checks no verifiers clash? otherwise they might override each others storage and you look yourself out of your account potentially.

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.

Yes, this is a great idea

Comment thread pkg/contracts/src/WorldChainAccount.sol
Comment thread pkg/contracts/src/WorldChainAccount.sol
Comment thread pkg/contracts/src/WorldChainAccount.sol
/// and running each verifier's installation hook. The hash rotation atomically retires
/// every entry from the prior key ring without an explicit per-element wipe.
function _installKeyRing(WorldChainAccountVerifier[] calldata sessionVerifiers) internal {
IWorldChainAccount.KeyRingStorage storage $ = _keyRingStorage();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

as mentioned on slack, we should revisit this!

import {IWorldChainAccountRouterErrors} from "../interfaces/IWorldChainAccountRouterErrors.sol";

/// @title Dispatch
/// @author 0xOsiris, World Contributors
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

it better not have a vulnerability then xD

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants