Skip to content

feat: WalletStateHook — condition-based wallet-state gating for _preFund#33

Open
douglasborthwick-crypto wants to merge 4 commits intoerc-8183:mainfrom
douglasborthwick-crypto:feat/wallet-state-hook
Open

feat: WalletStateHook — condition-based wallet-state gating for _preFund#33
douglasborthwick-crypto wants to merge 4 commits intoerc-8183:mainfrom
douglasborthwick-crypto:feat/wallet-state-hook

Conversation

@douglasborthwick-crypto
Copy link
Copy Markdown

Summary

Adds a minimal ERC-8183 hook that gates the fund stage on a condition-based wallet-state verifier. Complements existing score-based gating (TrustGateHook, #9/#32) and content-based verification (ReasoningVerifierHook, #31) with a third shape: does this wallet satisfy a named condition set right now?

  • Score-based (TrustGateHook) — "reputation ≥ N"
  • Content-based (ReasoningVerifierHook) — "output verified, confidence ≥ N"
  • Condition-based (WalletStateHook, this PR) — "wallet passes condition set X (e.g. USDC ≥ 1000 on Base, KYC attested, NFT held)"

What's added

File Purpose
contracts/interfaces/IWalletStateVerifier.sol Minimal (bool verified, uint256 validUntil) interface keyed on (wallet, conditionsHash). Hooks remain stateless views.
contracts/hooks/WalletStateHook.sol Inherits BaseERC8183Hook + IERC8183HookMetadata. Immutable verifier + conditionsHash (deploy one hook per distinct set, mirrors the minConfidence pattern in #31). Overrides _preFund only.
contracts/examples/InsumerWalletStateVerifier.sol Reference IWalletStateVerifier implementation. Relayer-push model with optional RIP-7212 P256VERIFY precompile verification of off-chain ECDSA P-256 (ES256) attestation signatures.
test/WalletStateHook.t.sol 21 tests, all passing.

Design intent

Keep the core hook small, generic, and aligned with the current hook stack:

  • BaseERC8183Hook-native
  • Verifier-agnostic (interface-only dependency)
  • Minimal surface area — overrides _preFund only
  • Product-specific example separated from the core hook under contracts/examples/

Gate stage

_preFund — gates the client wallet before escrow forms. Parallels TrustGateHook's _preFund stage (score threshold) and ReasoningVerifierHook's _preSubmit stage (deliverable hash), so readers see a clean three-way contrast at distinct lifecycle points.

Verification model

The example verifier supports two modes:

  1. Optional RIP-7212 verification — if a P-256 public key is configured at construction, the relayer's submission must carry the off-chain attestation signature components (r, s, messageHash), which are verified via the RIP-7212 precompile before the attestation is stored. Zero trust in the relayer.
  2. Trusted relayer fallback — if pubKeyX = pubKeyY = 0, signature verification is skipped and the relayer is trusted. Useful for testnets or deployments where off-chain signature verification is performed by an independent auditor.

RIP-7212 is live on Base, Optimism, Arbitrum, Polygon, Scroll, ZKsync, Celo — the standard ERC-8183 L2 footprint.

Verification

  • forge build ✅ (only pre-existing lint notes matching current codebase style)
  • forge test --match-path test/WalletStateHook.t.sol ✅ 21/21 pass

Dependency on #30

This branch is stacked on #30 (IACPHook → IERC8183Hook rename) so the new files compile against the post-rename base. Will rebase cleanly once #30 merges.

douglasborthwick-crypto added a commit to douglasborthwick-crypto/hook-contracts that referenced this pull request Apr 18, 2026
…w, credentials

Follow-ups per CONTRIBUTING.md on PR erc-8183#33:

- WalletStateHook.sol: expand NatSpec to match BiddingHook/FundTransferHook style
  with USE CASE / FLOW / TRUST MODEL sections + Profile A label.

- InsumerWalletStateVerifier.sol: add credentials block covering both paths —
  developers via POST /v1/keys/create (email-based, free tier) and agents via
  POST /v1/keys/buy (wallet-based, no email, USDC/BTC payment proves identity).

- README.md: add WalletStateHook row to the Hook Examples table, Profile A.

No code changes — docs only. forge build clean, forge test 21/21 pass.
Adds a minimal ERC-8183 hook that gates the fund stage on a condition-based
wallet-state verifier. Complements existing score-based gating (TrustGateHook,
erc-8183#9/erc-8183#32) and content-based verification (ReasoningVerifierHook, erc-8183#31) with a
third shape: "does this wallet satisfy a named condition set right now?"

- contracts/interfaces/IWalletStateVerifier.sol
    Minimal (bool verified, uint256 validUntil) interface keyed on
    (wallet, conditionsHash). Hooks stay stateless views.

- contracts/hooks/WalletStateHook.sol
    Inherits BaseERC8183Hook + IERC8183HookMetadata. Immutable verifier +
    conditionsHash (deploy one hook per distinct condition set, mirrors the
    minConfidence immutable pattern in ReasoningVerifierHook). Overrides
    _preFund only — verifier.checkWalletState(caller, conditionsHash) →
    pass/fail + freshness, reverts otherwise.

- contracts/examples/InsumerWalletStateVerifier.sol
    Reference IWalletStateVerifier implementation. Relayer-push model with
    optional RIP-7212 P256VERIFY precompile verification of off-chain ECDSA
    P-256 (ES256) attestation signatures. Works on Base, Arbitrum, Optimism,
    Polygon, Scroll, ZKsync, Celo — standard ERC-8183 L2 footprint.

- test/WalletStateHook.t.sol
    21 tests, all passing. Covers constructor guards, _preFund happy path,
    not-verified revert, expired-attestation revert, validUntil boundary,
    selector isolation, ERC-165 interface support, verifier relayer auth,
    and signature-mode flag.

Stacked on top of erc-8183#30 (IACPHook → IERC8183Hook rename). Targets main;
will rebase cleanly once erc-8183#30 merges.
…w, credentials

Follow-ups per CONTRIBUTING.md on PR erc-8183#33:

- WalletStateHook.sol: expand NatSpec to match BiddingHook/FundTransferHook style
  with USE CASE / FLOW / TRUST MODEL sections + Profile A label.

- InsumerWalletStateVerifier.sol: add credentials block covering both paths —
  developers via POST /v1/keys/create (email-based, free tier) and agents via
  POST /v1/keys/buy (wallet-based, no email, USDC/BTC payment proves identity).

- README.md: add WalletStateHook row to the Hook Examples table, Profile A.

No code changes — docs only. forge build clean, forge test 21/21 pass.
@ariessa
Copy link
Copy Markdown
Collaborator

ariessa commented May 4, 2026

Hi @douglasborthwick-crypto,

  1. This PR is outdated. Can you pull current code from main branch into this PR?
  2. This hook doesn't support MultiHookRouter. If WalletStateHook supports MultiHookRouter, it can be used with n number of hooks where n is the max hooks per job. Can you make the hook support MultiHookRouter?
  3. This PR contains more than just a hook contract, it also has an interface file named IWalletStateVerifier.sol. Can you move the interface inside the hook contract instead?
  4. This PR is bloated. Can you remove all unnecessary files except for the hook contract?
  5. What is the use case of this hook?
  6. What kind of problem is it trying to solve?

…MultiHookRouter compat

Addresses review on erc-8183#33:
- Inline IWalletStateVerifier into WalletStateHook.sol
- Remove example verifier (re-hosted in insumer-examples)
- Add MULTIHOOKROUTER NatSpec block; requiredSelectors() empty
- Tighten FLOW NatSpec to separate off-chain attestation source from
  on-chain IWalletStateVerifier surface
- README row updated to verifier-agnostic phrasing
@douglasborthwick-crypto
Copy link
Copy Markdown
Author

Hi @ariessa, addressed in 420b72b.

1. Outdated. Branch is at current main HEAD — git rev-list --left-right --count origin/main...feat/wallet-state-hook returns 0 3, and git rebase origin/main is a no-op. GitHub may have been showing stale state; should re-eval now that there's a new commit on the branch.

2. MultiHookRouter support. The hook is router-compatible by construction:

  • BaseERC8183Hook.onlyERC8183(jobId) already supports the router context (see contracts/BaseERC8183Hook.sol — modifier accepts either msg.sender == erc8183Contract standalone, or msg.sender == job.hook behind a router). Deploy with erc8183Contract_ = routerAddress.
  • IERC8183HookMetadata.requiredSelectors() returns an empty array — no cross-selector dependencies — so router _validateSelectorCompleteness passes when configured solely on fund.
  • Added MULTIHOOKROUTER NatSpec block on WalletStateHook documenting both points.

3. Interface inside the hook. Done. IWalletStateVerifier is now declared at the top of contracts/hooks/WalletStateHook.sol. Standalone contracts/interfaces/IWalletStateVerifier.sol removed.

4. Bloat. Removed:

  • contracts/examples/InsumerWalletStateVerifier.sol — the reference verifier was implementation-specific and is now hosted separately. Out of scope for the canonical hook contract.
  • The corresponding InsumerWalletStateVerifierTest block in test/WalletStateHook.t.sol.
  • README link to the example verifier; updated to verifier-agnostic phrasing.

PR now ships three files: contracts/hooks/WalletStateHook.sol, test/WalletStateHook.t.sol, and one row in README.md.

5. Use case. Pre-escrow gating on a deterministic wallet-state condition set. The hook checks "does this wallet satisfy the named conditions right now?" before fund can lock the budget. Examples: stablecoin balance threshold on a specific chain, NFT held, KYC attestation present, a chained mix.

6. Problem. Many ERC-8183 jobs need a wallet-policy check before escrow forms — today this happens off-chain in integration code or via custom on-chain logic per project. WalletStateHook is the third hook shape sitting alongside score-based gating (TrustGateHook — "reputation ≥ N") and content-based gating (ReasoningVerifierHook — "deliverable verified"): score / content / wallet state, all composable in MultiHookRouter, all sharing the same BaseERC8183Hook lifecycle.

— Douglas

@ariessa
Copy link
Copy Markdown
Collaborator

ariessa commented May 5, 2026

Hi @douglasborthwick-crypto,

Thank you for this contribution, and for working through the review feedback with me. I genuinely appreciate the back-and-forth.

While reviewing, I noticed that #9 addresses the same problem and takes a more general approach that also covers the case your PR handles. Rather than splitting review effort across two overlapping PRs, it might make more sense to consolidate the work there — either by reviewing the other overlapping PR, suggesting improvements, or contributing follow-up changes once it lands.

@douglasborthwick-crypto
Copy link
Copy Markdown
Author

Hi @ariessa, thanks for the close read. Quick note on the boundary — #9 and #33 are sibling primitives rather than overlapping ones:

@rnwy mapped this directly in #9 (split-shape note) as the score / condition / content three-way that all sit on BaseERC8183Hook. @psmiratisu raised the wallet-risk vs. agent-quality split as an acceptable outcome on Apr 15 in the same thread.

— Douglas

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants