Skip to content

Martin/zkpassport demo - #8059

Draft
mverzilli wants to merge 11 commits into
Uniswap:mainfrom
mverzilli:martin/zkpassport-demo
Draft

Martin/zkpassport demo#8059
mverzilli wants to merge 11 commits into
Uniswap:mainfrom
mverzilli:martin/zkpassport-demo

Conversation

@mverzilli

Copy link
Copy Markdown

No description provided.

mverzilli and others added 10 commits September 2, 2026 11:46
Integrates the ZKPassportAttest registry into the Launches flow:

- Creator wizard: a 'Use ZKPassport' option sources the policy dropdown
  from on-chain PolicyCreated logs and links out for custom policies.
- Auction page: a Verify button opens the ZKPassport popup with the
  policyId read from the auction's validation hook, and eligibility
  refreshes from the popup's postMessage result without a reload.
- Popup and policy-creator URLs are env-overridable
  (ZKPASSPORT_POPUP_URL, ZKPASSPORT_CREATE_POLICY_URL) with localhost
  fallbacks for dev.

Also carries the workspace unblocks the build needs on a fresh checkout:
nx.json/bun.lock updates and tsconfig/package stubs for workspace
projects absent from this fork.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQ9i1sp3EJA9wwPAViJS7X
On push to this branch, GitHub Actions builds the Vercel bundle
(SKIP_CONFIG_PULL with a generated .env.override), injects an edge
middleware that gates the whole SPA and /api proxy behind
BASIC_AUTH_CREDENTIALS from the Vercel project env, and ships it with
vercel deploy --prebuilt. Requires repo secrets VERCEL_TOKEN,
VERCEL_ORG_ID, VERCEL_PROJECT_ID and repo variables
ZKPASSPORT_POPUP_URL, ZKPASSPORT_CREATE_POLICY_URL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQ9i1sp3EJA9wwPAViJS7X
The client picks proxy-vs-direct gateway URLs from
config.enableEntryGatewayProxy, which is baked from the resolved .env
files at build time — a shell env var alone never reaches the bundle.
Uniswap's own Vercel builds get it from the pulled staging config, which
this workflow skips, so the deployed bundle called
entry-gateway.backend-staging directly and CORS blocked every
/rpc/<chainId> call (auction data, ZKPassport policy log scans). Baking
ENABLE_ENTRY_GATEWAY_PROXY into the generated .env.override routes those
calls through the same-origin Hono proxy instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQ9i1sp3EJA9wwPAViJS7X
Third-party deployments of this interface cannot reach the liquidity
backend (no CORS), which only serves as a calldata builder for bids —
the bidder's wallet signs and sends the transaction either way. Behind
ZKPASSPORT_ONCHAIN_BIDS (baked at build; the deploy workflow enables
it), submitBid calldata is encoded locally against the CCA auction
contract, whose bidding is documented as a permissionless direct call.
The encoder is pinned by a test reproducing a real sepolia bid's
backend-built calldata byte for byte.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQ9i1sp3EJA9wwPAViJS7X
Behind ZKPASSPORT_ONCHAIN_LAUNCH (baked at build; the deploy workflow
enables it), auction creation maps the wizard's existing request onto
Uniswap's public @uniswap/liquidity-launcher-sdk instead of the
CORS-blocked liquidity backend — the SDK assembles the same launcher
multicall the backend builds, verified byte-for-byte against a real
sepolia launch transaction. Scope matches the demo: new tokens with an
optional validation hook; existing-token launches and liquidity locks
throw a descriptive error through the existing failure surface.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQ9i1sp3EJA9wwPAViJS7X
The web app routes every chain read through UniRPC
(/entry-gateway/rpc/<chainId>), which 401s until a gateway session is
established. Viewers without one (fresh profile, failed Turnstile
challenge on a third-party domain) still see the auction page — its data
rides sessionless connect endpoints — but the zk hook introspection
fails, isGated stays false, and the bid form shows a spurious "Auction
not supported" banner.

Move the gate's reads (hook erc1155()/tokenId(), registry balanceOf) to
a public client built from the chain's Default/Fallback RPC URLs, which
need neither a session nor an API key. Also stop the backend
verify-wallet outcome from disabling the bid button on zk-gated
auctions: the hook validates on-chain, and VerifyWallet rides the
CORS-blocked liquidity backend on third-party origins.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQ9i1sp3EJA9wwPAViJS7X
The mobile app rejects proofs whose claimed domain differs from the
popup's actual origin, and the registry domain is constructor-fixed, so
a second registry was deployed with the popup's real host
(zkpassport-attest-popup.vercel.app) as its domain.

Make the registry address and deploy block overridable via
ZKPASSPORT_ATTEST_REGISTRY_SEPOLIA / ZKPASSPORT_ATTEST_DEPLOY_BLOCK_SEPOLIA
repo variables so future rewires only need a workflow re-run, not a
commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQ9i1sp3EJA9wwPAViJS7X
The launch success screen navigated to the build-time CREATE2
prediction, which can diverge from the deployed address: the LBP
strategy derives its own factory salt and patches the auction params
before create, so replaying factory.getAddress client-side does not
always hash to the same address (observed on sepolia: predicted
0x46dA7929… vs deployed 0x44284947…).

Resolve the deployed address from the AuctionCreated log in the launch
receipt over the sessionless client, keeping the prediction as the
fallback when the receipt cannot be fetched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQ9i1sp3EJA9wwPAViJS7X
The bid review modal simulates every bid against the liquidity
backend's SubmitBid before submitting, which is CORS-blocked from
third-party origins — so the review flow stalled before ever reaching
the wallet. With ZKPASSPORT_ONCHAIN_BIDS the calldata is encoded
locally, the CCA contract enforces price validity, and the wallet's
gas estimation surfaces reverts before signing, so the dry-run adds
nothing there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQ9i1sp3EJA9wwPAViJS7X
with:
node-version-file: .nvmrc

- uses: oven-sh/setup-bun@v2

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:
An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA. Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload. Github, foundry, and uniswap made github actions are exempt.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
- uses: oven-sh/setup-bun@v2
- uses: oven-sh/setup-bun@<VERIFIED_VALUE_REQUIRED> # v2
with:
bun-version: 1.3.14
View step-by-step instructions
  1. Identify the trusted commit SHA for the v2 release of oven-sh/setup-bun from its official repository or release tag.
  2. Replace the mutable tag with the full 40-character commit SHA and keep the release as a comment for readability:
    - uses: oven-sh/setup-bun@<40-character-commit-sha> # v2
  3. Keep bun-version: 1.3.14 unchanged so the workflow continues to install the intended Bun version.
💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by third-party-action-not-pinned-to-commit-sha-foundry-allowed.

You can view more details about this finding in the Semgrep AppSec Platform.

The popup page opens with full browser chrome (no window features), and
the attest request no longer sends walletAddress: the recipient account
is connected and selected inside the popup, which reports it back in the
outcome. The gate still checks the wallet connected here, so it unlocks
only when the chosen account matches.

Claude-Session: https://claude.ai/code/session_01AgsZAYrEuYPXZqhZ5Ahajr
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