Skip to content

feat(viem-chain): anchor wallet/public clients to chainId at construction - #29

Merged
Hiksang merged 2 commits into
mainfrom
hardening/viem-chain-anchor
May 8, 2026
Merged

feat(viem-chain): anchor wallet/public clients to chainId at construction#29
Hiksang merged 2 commits into
mainfrom
hardening/viem-chain-anchor

Conversation

@Hiksang

@Hiksang Hiksang commented May 8, 2026

Copy link
Copy Markdown
Collaborator

Anchors viem wallet and public clients to a known chainId at construction time, preventing accidental cross-chain RPC reads/broadcasts when an env var or registry mismatch surfaces.

🤖 Generated with Claude Code

Hiksang added 2 commits May 5, 2026 19:08
…tion

SSOT 7.4 hardening: every viem WalletClient / PublicClient built by the
CLI now carries an explicit `chain` parameter pinned to the chains.toml
entry, instead of relying on viem's auto-fetch of `eth_chainId` from
the RPC. This defends against:

- MITM RPCs that lie about eth_chainId (returning a chainId for a fork
  the user did not intend to broadcast on).
- Offline-signing flows that need a deterministic chainId baked into
  the signature without an extra RPC round-trip.
- RPC drift / endpoint reassignment between client construction and
  the actual broadcast.

Changes:

1. defi-core/src/registry/chain.ts
   - ChainConfig.viemChain() builds a viem-compatible Chain from the
     toml entry: id (chain_id), name, nativeCurrency (native_token,
     18 decimals), rpcUrls.default.http (effectiveRpcUrl()), optional
     blockExplorers (explorer_url) and contracts.multicall3.
   - Returns a local ViemChainShape interface so defi-core stays
     viem-agnostic at the package boundary; consumers cast to viem's
     Chain when they wire the result.

2. defi-core/src/provider.ts
   - getProvider(rpcUrl, chain?) — when chain is provided, the cache
     key becomes `${rpcUrl}@${chain.id}` so two callers with the same
     RPC but different anchors don't collide on cached client.

3. defi-cli/src/executor.ts
   - Executor constructor accepts an optional 4th `chain: Chain` arg,
     stored as readonly. Backwards-compatible — existing callers
     (executor.test.ts, scripts) don't pass it and continue to work.
   - All five createPublicClient / createWalletClient call sites now
     spread chainOpt() so the anchor flows through fee fetching,
     simulation, allowance probing, and broadcast.

4. defi-cli/src/cli.ts
   - makeExecutor() pulls the ChainConfig from the registry and threads
     `chain.viemChain()` into the new Executor 4th arg.

5. defi-core/src/registry/chain.test.ts (new, 4 tests)
   - viemChain() returns full viem Chain shape with all required fields.
   - viemChain() omits optional explorer / multicall3 when missing.
   - viemChain() honors env-var RPC overrides via effectiveRpcUrl().
   - chainId is preserved verbatim — a config typo (chain_id = 0)
     surfaces immediately rather than auto-fetched at runtime.

Verified: monorepo build/test/lint all clean — 82/82 tests pass
(defi-core 32 + defi-protocols 21 + defi-cli 29).

Refs: F3 in docs/qa-reports/2026-05-05-test-foundation.md.
@Hiksang
Hiksang merged commit bca063e into main May 8, 2026
3 checks passed
@Hiksang
Hiksang deleted the hardening/viem-chain-anchor branch May 8, 2026 11:56
Hiksang added a commit that referenced this pull request May 8, 2026
…ion + adapter fixes (#32)

* build: regenerate dist + sync prebuild package configs for v1.0.13

Re-runs `pnpm -r build` from current main HEAD and syncs the package-
local config mirror so npm-published artifacts match the source tree.

Reason: across PRs #23 / #26 / #27 we resolved dist conflict markers
by taking main's stale dist (`git checkout --theirs`) — that kept the
PRs mergeable but left committed dist files out of sync with the
post-merge source. This single rebuild commit reconciles them.

Affected:
  defi-cli/dist/{index,main,mcp-server}.js  (~+700 lines source-derived)
  defi-cli/dist/*.map
  defi-protocols/dist/{index.js, index.d.ts, *.map}
  defi-core/dist/{index.d.ts, *.map}
  defi-cli/config/protocols/lending/venus_flux_bnb.toml
    (prebuild sync from workspace root: vusdt → vlisusd rename)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Release v1.0.13: bridge feature + production-grade verification + adapter fixes

Highlights since v1.0.12 (30 commits across 8 PRs):

== Bridge feature complete ==
- Relay added as 4th provider (~3s native bridge, executor-wired live)
- LiFi + deBridge + CCTP wired to Executor — `--broadcast` no longer silently ignored
- CCTP V2 `--auto-receive` — burn → poll Iris attestation → auto-receiveMessage on dest
- Live-verified Base→Arbitrum 0.1 USDC E2E (burn 0x6b5d41fd, receive 0x9b5bd5c2)
- Per-chain src/dst token resolution for cross-chain stables
- Relay errorCode parsing → actionable hints (AMOUNT_TOO_LOW, NO_QUOTES, etc)
- MCP `defi_bridge` 3 latent fixes: dst-token, Relay symbol→native, LiFi fromAddress

== Adapter bug fixes ==
- compound_v2: uint256.max withdraw + outstanding-borrow guard (Venus, Compound V2 forks)
- venus-flux-bnb: rename mislabeled `vusdt` → `vlisusd` (actual underlying is lisUSD)
- Ramses CL mint encoding + MerchantMoe/TraderJoe LB token-order realign
- Curve target the pool not router + StableswapNG dynamic uint256[] ABI
- uniswap-v2 / solidly LP-token approval on remove
- Hybra V4 --redeem-type CLI flag for instant-exit vs 2-year veHYBR lock
- Aave V3 collateral toggle + eMode for borrow lifecycle (#21)
- Morpho marketId-based supply/borrow + supplyCollateral (#22)
- Compound V2 enterMarkets toggle for Venus borrow lifecycle (#26)
- Native wrap/unwrap CLI + Morpho marketId registry + max-repay-by-shares (#27)

== Production-grade verification sweep ==
- HyperEVM 11/11 protocols full lifecycle (emission tokens received)
- Mantle: Aave V3 + UniV3 + MerchantMoe LB (MOE 1.009 received)
- Base: UniV3 + Aerodrome V2 + Aerodrome CL (AERO emission received)
- BNB upgraded to 🟢 production: 13/16 protocols verified live
- Monad partial: UniV3 + UniV2 (mainnet TBD for the rest)
- ve(3,3) emission tokens live-received: RAM, KITTEN, MOE, AERO, THE
- ULTRAQA sandbox script (51-test sweep across 4 chains)

== Hardening ==
- ip-address >=10.1.1 via pnpm.overrides (GHSA-v2v4-37r5-5v8g moderate XSS)
- viem wallet/public clients anchored to chainId at construction (#29)
- CLI handler unit-test coverage for status/schema (#28)
- @vitest/coverage-v8 baseline measurement script (#30)
- post-baseline test-foundation QA report (#31)

== Test plan ==
- pnpm -r test: 189/189 pass (defi-core 32, defi-protocols 55, defi-cli 102)
- pnpm -r build: clean across all 3 packages
- pnpm audit: 0 vulnerabilities
- ULTRAQA sandbox: 51/51 pass + 8/8 ve(3,3) lp pipeline
- CCTP --auto-receive E2E: live broadcast verified
- MCP defi_bridge smoke: LiFi USDC + Relay USDC + Relay native — all OK

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant