Skip to content

rpc urls - #184

Closed
0xthrpw wants to merge 7 commits into
grailsmarket:mainfrom
0xthrpw:rpc-urls
Closed

0xthrpw wants to merge 7 commits into
grailsmarket:mainfrom
0xthrpw:rpc-urls

Conversation

@0xthrpw

@0xthrpw 0xthrpw commented Jul 9, 2026

Copy link
Copy Markdown
Member

env-driven full RPC URLs, drop alchemy/quicknode plumbing

  • transports move to server-safe src/lib/rpc.ts (no RainbowKit — the OG route reaches them via getEtherPrice) and read NEXT_PUBLIC_{MAINNET,BASE,OPTIMISM}_RPC_URL with free public fallbacks
  • base/optimism transports stay: EFP list-settings resolves the list's storage-location chain and needs them; they no longer need paid IDs
  • fix mainnet default block explorer (pointed at explorer.base.org)
  • Dockerfile args + .env.example swapped to the *_RPC_URL vars

@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown

@0xthrpw is attempting to deploy a commit to the EthID Team on Vercel.

A member of the Team first needs to authorize it.

@0xthrpw 0xthrpw closed this Jul 9, 2026
@0xthrpw
0xthrpw deleted the rpc-urls branch July 9, 2026 01:26
@greptile-apps

greptile-apps Bot commented Jul 9, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces provider-specific RPC ID plumbing (Alchemy/QuickNode) with full env-driven RPC URLs, moves transports into a new server-safe src/lib/rpc.ts module free of RainbowKit imports, and adds a committed .env.production + Dockerfile for Docker-based deployments. It also updates contract addresses to production deployments and adds a prices: bigint[] parameter to the BulkRegistrar multiRegister call.

  • RPC refactor: src/lib/rpc.ts exposes a single transports map consumed by both server code (OG routes, getEtherPrice) and client code (wagmi config, hooks); all hardcoded grails.app URLs in OG routes are replaced with the env-driven APP_URL.
  • BulkRegistrar update: ABI, useBulkRegisterDomains, and use-registration-modal are updated to pass per-name prices[] to multiRegister, matching a new contract interface.
  • Docker plumbing: A new multi-stage Dockerfile builds a standalone Next.js image; .env.production is committed intentionally as public build-time config, with secrets expected from the deploy platform at runtime.

Confidence Score: 4/5

Safe to merge with minor follow-ups; the RPC transport refactor is clean and the Docker plumbing is well-considered.

The RPC transport extraction is straightforward and the server/client boundary reasoning is sound. The .env.example has three documentation gaps that could confuse local dev setup. The dual-RPC-call pattern in the registration modal introduces a small but real window for price divergence that could cause a transaction revert, and makes an unnecessary round trip that could be eliminated by summing the per-name prices directly.

.env.example needs the stale comment, duplicate key, and missing NEXT_PUBLIC_GRAILS_API_URL fixed. src/components/modal/registration/hooks/use-registration-modal.ts deserves a second look around the dual pricing calls.

Important Files Changed

Filename Overview
src/lib/rpc.ts New server-safe transport module; clean separation from RainbowKit, but wraps a single transport in fallback when no env URL is set — functionally correct but unnecessarily complex.
src/lib/wagmi.ts Transports moved to rpc.ts; wagmi.ts now re-exports them. Mainnet block explorer URL corrected from explorer.base.org to eth.blockscout.com.
.env.example Old Alchemy/QuickNode IDs replaced with *_RPC_URL vars; stale wagmi.ts comment, duplicate NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID entry, and missing NEXT_PUBLIC_GRAILS_API_URL needed by the kit's server-side paths.
.env.production Intentionally committed public build-time config for Docker; well-documented with clear separation from secrets injected at deploy time.
Dockerfile New multi-stage Docker build using bun + Next.js standalone output; ARG/ENV plumbing for NEXT_PUBLIC_* vars is correct, vendor tarball layer order is intentional.
src/app/providers.tsx Module-level setIdentityKitApiUrls calls configure both the kit's main and utils entry points with correct env vars; runs before any kit fetch since it wraps the whole tree.
src/components/modal/registration/hooks/use-registration-modal.ts Adds per-name prices array to multiRegister call (matching updated ABI), but makes two independent RPC calls for pricing without reconciling totals, creating a small window for price divergence.
src/hooks/registrar/useBulkRegisterDomains.ts Correctly threads new prices: bigint[] parameter through both estimateContractGas and writeContract calls to match updated BulkRegistrar ABI.
src/constants/web3/contracts.ts Contract addresses updated to production deployments (bulk renewal, referrer, bulk registrar, conduit); removes TODO placeholder comments.
src/utils/web3/getEtherPrice.ts Now uses the shared transport from rpc.ts instead of a hardcoded Alchemy URL; eliminates the NEXT_PUBLIC_MAINNET_ALCHEMY_ID dependency in this file.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Server as Server (OG routes / API)
    participant RPC as src/lib/rpc.ts
    participant Wagmi as src/lib/wagmi.ts
    participant Client as Browser Client

    Note over RPC: Reads NEXT_PUBLIC_*_RPC_URL<br/>(build-time inlined)

    Server->>RPC: "import { transports }"
    RPC-->>Server: fallback([envUrl?, publicUrl])
    Server->>Server: "createPublicClient({ transport: transports[mainnet.id] })"
    Server->>Server: getEtherPrice() via Chainlink

    Wagmi->>RPC: "import { transports }"
    RPC-->>Wagmi: same transport map
    Wagmi->>Wagmi: "createConfig({ transports })"
    Wagmi-->>Client: WagmiProvider (SSR + hydration)

    Client->>Client: providers.tsx module init
    Client->>Client: setIdentityKitApiUrls(efpApiUrl, grailsApiUrl, ensMetadataUrl)
    Client->>Client: setIdentityKitUtilsApiUrls(...)

    Client->>RPC: use-list-settings, getEfpProfileRoles, ens.ts
    RPC-->>Client: transports[mainnet/base/optimism]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Server as Server (OG routes / API)
    participant RPC as src/lib/rpc.ts
    participant Wagmi as src/lib/wagmi.ts
    participant Client as Browser Client

    Note over RPC: Reads NEXT_PUBLIC_*_RPC_URL<br/>(build-time inlined)

    Server->>RPC: "import { transports }"
    RPC-->>Server: fallback([envUrl?, publicUrl])
    Server->>Server: "createPublicClient({ transport: transports[mainnet.id] })"
    Server->>Server: getEtherPrice() via Chainlink

    Wagmi->>RPC: "import { transports }"
    RPC-->>Wagmi: same transport map
    Wagmi->>Wagmi: "createConfig({ transports })"
    Wagmi-->>Client: WagmiProvider (SSR + hydration)

    Client->>Client: providers.tsx module init
    Client->>Client: setIdentityKitApiUrls(efpApiUrl, grailsApiUrl, ensMetadataUrl)
    Client->>Client: setIdentityKitUtilsApiUrls(...)

    Client->>RPC: use-list-settings, getEfpProfileRoles, ens.ts
    RPC-->>Client: transports[mainnet/base/optimism]
Loading

Comments Outside Diff (2)

  1. .env.example, line 15-18 (link)

    P2 Duplicate env-var declaration

    NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID= appears on both lines 15 and 18. The first occurrence will be shadowed/ignored by most dotenv parsers, but it creates confusion for developers copying values into .env.local.

    Fix in Conductor Fix in Cursor Fix in Codex Fix in Claude Code

  2. .env.example, line 1-34 (link)

    P2 NEXT_PUBLIC_GRAILS_API_URL missing from developer template

    .env.production documents that the ethereum-identity-kit reads NEXT_PUBLIC_GRAILS_API_URL for its "module-init and server-side paths". That variable does not appear in .env.example, so developers working locally will have it undefined at build time. Client-side calls are rescued by setIdentityKitApiUrls in providers.tsx, but any server-side kit initialisation (SSR page renders, API routes that touch the kit) will silently fall back to whatever default URL the vendored tarball has compiled in, which may differ from api.relics.bid.

    Fix in Conductor Fix in Cursor Fix in Codex Fix in Claude Code

Fix All in Conductor Fix All in Cursor Fix All in Codex Fix All in Claude Code

Reviews (1): Last reviewed commit: "env rpc urls" | Re-trigger Greptile

Comment thread .env.example
NEXT_PUBLIC_OPTIMISM_ALCHEMY_ID=
NEXT_PUBLIC_OP_SEPOLIA_ALCHEMY_ID=
# Full RPC URLs (key/token embedded), e.g. https://<rpc-host>/hbx_rpc_...
# Unset chains fall back to free public endpoints (see src/lib/wagmi.ts).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Stale file reference in comment

The fallback RPC endpoints are now defined in src/lib/rpc.ts, not src/lib/wagmi.ts. A developer following this comment to understand fallback behaviour will look at the wrong file.

Fix in Conductor Fix in Cursor Fix in Codex Fix in Claude Code

Comment thread src/lib/rpc.ts
Comment on lines +15 to +16
const rpc = (envUrl: string | undefined, publicUrl: string) =>
fallback((envUrl ? [envUrl, publicUrl] : [publicUrl]).map((url) => http(url, { batch: true })))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 fallback with a single transport is a no-op

When envUrl is undefined, fallback([http(publicUrl, { batch: true })]) wraps a single transport in a fallback list. fallback only adds value with ≥ 2 transports; with one entry it behaves identically to a bare http() call but adds unnecessary overhead. Consider returning http(url, { batch: true }) directly when there is only one URL.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Conductor Fix in Cursor Fix in Codex Fix in Claude Code

Comment on lines 542 to +558
dispatch(setRegistrationError(`Failed to get price for batch ${batchIndex + 1}`))
return
}

const batchPrices = await getBulkRentPrices(batchLabels, batchDurations)
if (!batchPrices) {
dispatch(setRegistrationError(`Failed to get per-name prices for batch ${batchIndex + 1}`))
return
}

const valueWithBuffer = (batchPrice * BigInt(105)) / BigInt(100)

const tx = await submitMultiRegister(
batchLabels,
owner,
batchDurations,
batchPrices,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Two sequential RPC calls for pricing with no reconciliation

getBulkTotalPrice is called first to compute batchPrice (used for the ETH value buffer), then getBulkRentPrices is called separately to obtain the per-name prices array passed to multiRegister. These are two independent on-chain reads; if a price oracle update lands between them, the ETH value ceiling derived from the total could be lower than the sum the contract actually requires, causing the transaction to revert.

A simpler approach: derive the total from the already-fetched batchPrices array (batchPrices.reduce((a, b) => a + b, 0n)), eliminating the second RPC call and removing the timing window.

Fix in Conductor Fix in Cursor Fix in Codex Fix in Claude Code

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