Skip to content

feat: add TokenSafetyHook and ITokenSafetyOracle#20

Closed
JhiNResH wants to merge 3 commits into
erc-8183:mainfrom
JhiNResH:feat/token-safety-hook
Closed

feat: add TokenSafetyHook and ITokenSafetyOracle#20
JhiNResH wants to merge 3 commits into
erc-8183:mainfrom
JhiNResH:feat/token-safety-hook

Conversation

@JhiNResH
Copy link
Copy Markdown

@JhiNResH JhiNResH commented Mar 27, 2026

TokenSafetyHook v2

Gates job funding when payment token is flagged as unsafe by an external oracle.

Changes from v1

Issue Fix
Wrong FUND_SEL selector Inherit BaseACPHook, override _preFund — no manual selector routing
Token decoded from callback data (wrong field) Read paymentToken from getJob(jobId)
Upgradeable + provider-specific framing Non-upgradeable, aligned with base hook style

How it works

  1. fund(jobId, ...) triggers beforeAction_preFund
  2. Read job.paymentToken from AgenticCommerce.getJob(jobId)
  3. If whitelisted → pass through
  4. Else query ITokenSafetyOracle → if verdict is blocked → revert

Files

File What
contracts/hooks/TokenSafetyHook.sol Hook implementation (~130 lines)
contracts/interfaces/ITokenSafetyOracle.sol Oracle interface (vendor-agnostic)
test/TokenSafetyHook.t.sol 9 tests

Test plan

  • forge test --match-contract TokenSafetyHookTest — 9/9 pass
  • Review: _preFund reads token from job state, not callback data
  • Review: whitelist bypass, blocked verdict bitmask, admin access

@psmiratisu
Copy link
Copy Markdown
Contributor

@JhiNResH before i do some audit on the hook. Could i suggest some key things to fix ?

  • the fund selector needs to match the real current signature
    the token source should come from the actual job state or a clearly defined param path rather than from a decode path that can read the wrong value

it would be better to align this with the shared hook pattern and keep the implementation as close as possible to the base example style, and please remove any remaining provider-specific framing. Once that is cleaned up, happy to review a narrower v2. I would treat this as a standalone policy hook, not something that should be bundled with router or attestation paths.

…job state

v2 rewrite addressing review feedback:
- Inherit BaseACPHook, override _preFund only — no manual selector routing
- Read paymentToken from getJob(jobId) instead of decoding from callback data
- Remove upgradeable pattern — align with non-upgradeable hook style
- Remove provider-specific framing (security@maiat.io → security@erc-8183.org)
- 9 tests: safe/unsafe/whitelist/no-token/admin/batch/verdict-mask
@JhiNResH JhiNResH force-pushed the feat/token-safety-hook branch from bd3a633 to 36f7c1b Compare April 15, 2026 16:10
@JhiNResH
Copy link
Copy Markdown
Author

Rewrote from scratch. All three issues fixed:

  1. Selector — now inherits BaseACPHook, overrides _preFund only. No manual selector routing.
  2. Token source — reads paymentToken from AgenticCommerce.getJob(jobId), not from callback data.
  3. Style — non-upgradeable, removed Maiat-specific framing, standalone policy hook.

9 tests cover: safe token pass, honeypot revert, whitelist bypass, no-token skip, blocked verdict mask, admin access, batch whitelist.

Force-pushed — clean single commit on top of current main.

@ariessa
Copy link
Copy Markdown
Collaborator

ariessa commented May 4, 2026

Hi @JhiNResH,

  1. This PR is outdated. Can you pull current code from main branch into this PR?
  2. This hook doesn't support MultiHookRouter. If TokenSafetyHook 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 ITokenSafetyOracle.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?

@JhiNResH
Copy link
Copy Markdown
Author

JhiNResH commented May 4, 2026

Updated PR #20 in commit e73c86d.\n\nAddressed the requested changes:\n1. Pulled current upstream main into this branch.\n2. Added MultiHookRouter support by implementing IERC8183HookMetadata; requiredSelectors() now declares the fund selector.\n3. Moved ITokenSafetyOracle into TokenSafetyHook.sol as an inline minimal interface.\n4. Slimmed the PR down to a single file: contracts/hooks/TokenSafetyHook.sol.\n5. Use case: block unsafe ERC-20 payment tokens before job funds enter escrow.\n6. Problem solved: prevents honeypot/high-tax/blocked payment tokens from entering ERC-8183 job funding while keeping token-risk scoring external to the hook.

@ariessa
Copy link
Copy Markdown
Collaborator

ariessa commented May 5, 2026

Hi @JhiNResH,

Thanks for addressing the review feedback. I appreciate the time you put into the revisions. Closing this PR.

The hook duplicates the functionality of its own whitelisted mapping with an extra oracle layer that adds trust assumptions, failure modes, and per-fund gas — for marginal value in the ERC8183's threat model.

It also fails open in several places:

  • Unknown tokens decode to Safe (0) from a zero-initialized struct
  • Unverified (verdict 3) is not in the default block mask
  • No staleness check on oracle's lastUpdated value
  • setOracle is owner-callable with no delay
  • Check runs in _preFund but the token is committed in setBudget

Thanks for the engagement.

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.

3 participants