Skip to content

feat: support fee-token IGP enforcement in agents#8893

Draft
paulbalaji wants to merge 2 commits into
codex/igp-fee-token-contract-eventsfrom
codex/igp-fee-token-agent-support
Draft

feat: support fee-token IGP enforcement in agents#8893
paulbalaji wants to merge 2 commits into
codex/igp-fee-token-contract-eventsfrom
codex/igp-fee-token-agent-support

Conversation

@paulbalaji

@paulbalaji paulbalaji commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds the Rust agent side of exact non-native IGP fee-token support on top of #8892.

Changes:

  • extends InterchainGasPayment with fee_token and token-aware encoding/DB keys
  • indexes GasPaymentWithFeeToken in Ethereum/Tron while preserving legacy fallback behavior
  • adds scraper schema migration/storage for fee_token
  • enforces relayer gas payment policies by (messageId, destination, feeToken)
  • adds fail-closed config validation: non-zero feeToken requires origins with explicit latest/token-aware IGP metadata
  • updates SDK agent config schema for igpVersion and feeToken

Stack

Stacked on #8892, which adds/emits the token-aware IGP event. This PR should be reviewed after the contract/event PR.

Scraper Migration Notes

Adds scraper migration m20260611_000008_add_gas_payment_fee_token.

Migration up():

  • adds non-null gas_payment.fee_token as Address/bytea
  • backfills existing rows to native token with 0x0000000000000000000000000000000000000000
  • removes the temporary default after backfill
  • adds gas_payment_msg_id_fee_token_idx on (msg_id, fee_token)
  • recreates total_gas_payment grouped by (msg_id, fee_token) instead of only msg_id

Rollout notes:

  • apply the scraper DB migration before or with the scraper binary that writes fee_token
  • no manual backfill is needed for existing rows; they become native-token rows
  • downstream readers of total_gas_payment should expect one row per (msg_id, fee_token), not one row per msg_id
  • exact non-native fee-token enforcement still requires origins to be configured as latest/token-aware IGPs, so legacy origins fail closed for non-zero feeToken

Rollback notes:

  • down() recreates the legacy view only if all rows are native-token rows
  • rollback fails closed if any non-native fee_token rows exist, because dropping the column would merge distinct token amounts into one aggregate and corrupt totals
  • rolling back after non-native rows exist requires an explicit data plan or DB restore, not a blind migration rollback

Validation

  • cd rust/main && cargo test -p relayer fee_token_policy
  • cd rust/main && cargo test -p hyperlane-base db_native_token_key_reads_legacy_total_after_new_native_payment
  • cd rust/main && cargo check -p hyperlane-base -p relayer -p scraper
  • cd rust/main && cargo clippy -p hyperlane-base -p relayer -p scraper -- -D warnings
  • pnpm -C typescript/sdk exec mocha --config .mocharc.json ./src/metadata/agentConfig.test.ts --exit
  • git diff --check

@paulbalaji

Copy link
Copy Markdown
Collaborator Author

Review pass across the stacked PRs (#8892 + #8893). Findings below; I am going to remediate these next.

#8892 Solidity:

  • Low/test gap: native payments emit GasPaymentWithFeeToken(..., address(0), ...) through _payForGas, but the native payForGas test only expects legacy GasPayment. Add a native-path expectation so the zero-address sentinel is pinned.

#8893 agents/scraper/SDK:

  • Medium: message_view still aggregates gas_payment by msg_id only, so mixed native/ERC20 fee-token payments can produce ambiguous num_payments, total_payment, and total_gas_amount values across denominations. The new migration updates total_gas_payment, but not message_view.
  • Medium/Low: scraper migration leaves the temporary fee_token default in place. The modify_column(...not_null()) call does not drop the default; use explicit ALTER TABLE "gas_payment" ALTER COLUMN "fee_token" DROP DEFAULT and remove the redundant alter.
  • Medium/test-hardening: EVM/Tron event dedup is transaction-wide. Current Solidity invariant makes this safe today because latest IGP emits legacy + token-aware events together, but parser tests should pin legacy-only, native latest, ERC20 latest, and multi-payment behavior; field-level companion matching would be more robust.
  • Process: feat: support fee-token IGP enforcement in agents #8893 is stacked on a slash-containing base branch, and the main test/rust pull_request branch globs are *, so full CI is not gating this stacked PR. Need manual focused validation before relying on green checks.
  • Low: SDK changeset wording overstates TypeScript schema enforcement; Rust enforces latest IGP metadata, while Zod only accepts the optional fields. Also IgpVersion should be root-exported if public.

@paulbalaji paulbalaji force-pushed the codex/igp-fee-token-agent-support branch from 90b81e5 to 1242c62 Compare June 12, 2026 17:07
@paulbalaji

Copy link
Copy Markdown
Collaborator Author

Remediations pushed.

#8892:

  • Pushed 22d7d08e5c (test: assert native fee-token gas payment events) adding native GasPaymentWithFeeToken(..., address(0), ...) expectations for direct payForGas and native postDispatch.

#8893:

  • Rebased onto updated feat: emit fee-token gas payment events #8892 and pushed 1242c6299e (fix: harden fee-token gas payment indexing).
  • Scraper migration now explicitly drops the temporary fee_token backfill default and recreates message_view with native-only legacy gas totals; down migration drops/restores the dependent view before dropping fee_token.
  • EVM/Tron indexers now dedup only matching token-aware/legacy companion events by tx + message/payment fields, not by whole transaction; both packages have same-tx unmatched-legacy regression tests.
  • Added malformed feeToken parser coverage.
  • Exported IgpVersion from the SDK root and corrected the changeset wording.
  • Updated test and rust workflow PR branch globs to **, so slash-containing stacked base branches trigger full CI.

Local validation passed:

  • forge test --match-contract InterchainGasPaymasterTest --match-test 'testPayForGas|testPostDispatch_defaultGasLimit'
  • cargo test -p migration --lib
  • cargo test -p relayer fee_token_policy --lib
  • cargo test -p hyperlane-ethereum field_level_dedup_keeps_unmatched_legacy_payments_in_same_tx --lib
  • cargo test -p hyperlane-tron field_level_dedup_keeps_unmatched_legacy_payments_in_same_tx --lib
  • pnpm --dir typescript/sdk exec mocha --config .mocharc.json './src/metadata/agentConfig.test.ts' --exit
  • git diff --check
  • cargo fmt --check

CI state at last check: #8892 was green except one rust-e2e-tests / e2e-matrix (sealevel) job still in progress; #8893 full GitHub workflows were triggered and still in progress after the glob fix.

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

Labels

None yet

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

1 participant