Conversation
Implements a standalone Hono service (apps/relayer/) that allows users to vote and delegate on governance proposals without paying gas. The relayer validates EIP-712 signatures, checks eligibility and on-chain state, enforces rate limits, and submits transactions via a funded wallet. - Pluggable signer interface (local private key for v1, KMS-ready) - Supports castVoteBySig and delegateBySig for all supported DAOs - In-memory sliding window rate limiter (per-address hourly + daily) - Zod OpenAPI schemas with /health, /relay/vote, /relay/delegate routes - 29 tests across 7 files (unit + integration with MSW) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Captures the decision to move the relayer's rate-limit state from in-memory to the shared snapshot schema, including backfill-safety analysis, schema design, and the record-before-submit semantics. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reviewer-recommended clarifications: concrete reference SQL for the atomic reserveSlot INSERT, and an explicit note that rate-limiter service tests run against pglite (not mocks). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts: # apps/gateful/src/config.ts # pnpm-lock.yaml
Governance Relayer
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
🚅 Deployed to the anticapture-pr-1853 environment in anticapture-infra
16 services not affected by this PR
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 775d329013
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| await this.publicClient.simulateContract({ | ||
| address: this.governorAddress, | ||
| abi: governorAbi, | ||
| functionName: "castVoteBySig", | ||
| args: [params.proposalId, params.support, params.v, params.r, params.s], | ||
| account: await this.signer.getAddress(), | ||
| }); |
There was a problem hiding this comment.
Map simulation reverts to client-facing RelayError
simulateContract reverts for user-caused conditions (already voted, expired delegation, invalid proposal state/signature), but these exceptions are not converted to RelayError, so they fall through the global handler as 500 INTERNAL. In practice this makes normal request errors look like server outages and breaks API semantics for callers; the e2e cases for duplicate/late votes already demonstrate this path returning 500.
Useful? React with 👍 / 👎.
| const logger = createLogger("anticapture-relayer"); | ||
|
|
||
| async function main() { | ||
| const chain = mainnet; |
There was a problem hiding this comment.
Use configured chain instead of hardcoded mainnet
The service always builds viem clients with mainnet, while EIP-712 verification uses env.CHAIN_ID. If this relayer is deployed to any non-mainnet RPC (staging/testnet/another DAO chain), request handling can fail with chain mismatch behavior and signatures will be checked against a different chain context than the transaction client. This makes the CHAIN_ID config misleading and can break all relays outside chain ID 1.
Useful? React with 👍 / 👎.
No description provided.