Paymaster v0.7 + Arc Testnet + Permit2 donate + URL-driven network selection#11
Merged
Conversation
…n floor Prevents griefing attacks against an organization's CGPaymaster budget: - CGCrowdfunding enforces a min donation of 1 full token (10^decimals), resolved defensively from IERC20Metadata at deploy time. Sub-unit dust donations now revert, so each sponsored donate dwarfs its gas cost. - CGPaymaster._isValidCall decodes the inner selector from execute(addr, uint256,bytes) and only sponsors a fixed allowlist of donor and CGToken user ops, blocking the reverting-admin-call drain vector. - Frontend marks owner-only writes (createProgram, setBeneficiaries, execute, returnUntracked, sweepUntracked, ...) as sponsored:false so the EIP-5792 path is skipped for calls the paymaster would now reject. Also fixes pre-existing TypeScript errors that blocked check-types: narrow useTransactor's receipt via a local const, type the chains.find predicate, and remove a defunct openfort route reference. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contracts: switch CGPaymaster to PackedUserOperation, the v0.7 postOp signature (with actualUserOpFeePerGas + postOpReverted no-double-credit), and the v0.7 paymasterAndData layout that interleaves gas limits before our org-address blob. Outer-selector allowlist now accepts ERC-7579 execute(bytes32,bytes) alongside the SimpleAccount execute(address,uint256,bytes), so Kernel v3 accounts can be sponsored too. Refund is also capped defensively against a buggy entrypoint. Frontend: add a Kernel-via-Pimlico path for EOA wallets that cannot do EIP-5792 (services/web3/smartAccount.ts + useSponsoredUserOp). UserOps go through a new /api/bundler/[chainId] proxy that injects the server-side Pimlico key and allowlists only the methods permissionless needs. The paymaster route returns the v0.7 separate-fields response shape and rejects non-v0.7 entrypoints. UI: useEffectiveAddress resolves the on-chain identity (connected EOA when the wallet is itself a smart account, counterfactual Kernel otherwise) so donation lookups, allowance, balance, and the "View wallet" link all key off the address that actually appears as msg.sender on contributions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires Circle's Arc Testnet (chainId 5042002) into hardhat, scaffold target networks, SIWE verification, and the donation-currency catalog with the verified USDC/EURC ERC-20 addresses, and includes the initial CGRegistry / CGPaymaster deployment artifacts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Arc's official mark as the network-chip logo with a padded viewBox so it sits inside the rounded clip, inlines the per-chain overrides directly in scaffold.config, and restores the wagmi useAccount import that the Header was relying on. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Donations and donor refunds (cancelContribution / refund) can't honestly ride the paymaster: msg.sender must match the ERC-20 token holder, but USDC's permit is ECDSA-only, so a smart-account msg.sender can't permit- spend its own balance and can't pull tokens from the EOA either. Move both flows to direct EOA writes and pick the single-tx donateWithPermit path when currency.permit is set, falling back to approve + donate otherwise. Prune the now-unreachable donate/cancel/refund selectors from CGPaymaster's allowlist. Permit helper resolves the EIP-712 domain via EIP-5267 with a DOMAIN_SEPARATOR-matching fallback for older bytecode. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wallets render Permit2 signatures with a friendlier UI than raw ERC-2612 permits — the spender is the well-known Permit2 contract instead of an unverified per-campaign CGProgram. Adds donateWithPermit2 on CGProgram (minimal IPermit2 interface vendored inline) and switches the frontend to prefer it when getCode(PERMIT2_ADDRESS) shows the canonical deploy at 0x0000…22D473030F116dDEE9F6B43aC78BA3 is live on the active chain. Chains without Permit2 (localhost) fall back to the existing donateWithPermit path. Permit helper gains signPermit2 alongside the existing 2612 path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On Arc Testnet the native gas token is USDC, not ETH — surface the USDC mark in the native-asset row instead of the Ethereum diamond by looking up the logo from a symbol→asset map (falls back to ETH for any other chain). In the Receive USDC / Receive EURC top-up modal, when the connected chain is Arc Testnet, replace the CDP-proxied faucet button (which doesn't route to Arc) with an external link to Circle's faucet. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
viem 2.39.0 was below permissionless@0.3.5's ^2.44.4 peer-dep and @reown/appkit-adapter-wagmi@1.8.19's >=2.45.0 peer-dep, which made permissionless's toKernelSmartAccount silently produce account.address == zeroAddress for some Kernel derivation paths (the sender: 0x0 in UserOp dumps that turned into AA13). Pin to 2.51.3 which satisfies every dependent. Enabling viaIR shrinks every contract by 10-15% on the Yul IR pipeline. CGComponentFactory embeds creation bytecode for all three child contracts and bumped against the 24576-byte EIP-170 cap after the CGToken signed-approval additions; with viaIR it lands at 22149 bytes with margin to spare. burner-connector@0.0.20 ships its own pinned RainbowKit + @wagmi/core under node_modules/burner-connector/node_modules/. After the viem bump those nested copies emit a structurally identical Wallet/Connector type that TS treats as a distinct nominal type. Runtime is fine; cast at the connectorsForWallets boundary instead of forcing a yarn resolution. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CGToken gains setApprovalForAllWithSignature, an EIP-2612-style permit for ERC-1155 operator approval. SignatureChecker handles both ECDSA and ERC-1271, so both EOA and smart-account owners can authorize the sponsored Kernel as operator with an off-chain typed-data signature — no gas prep tx required. CGPaymaster's allowlist gains the new selector and now validates ERC- 7579 batch executions (callType 0x01): every inner call is checked against the same selector + org-ownership rules as single executions. Lets the frontend bundle setApprovalForAllWithSignature + the actual transfer into one sponsored UserOp. Frontend wiring: - useSponsoredUserOp.sendCalls / useSponsoredWrite.writeBatch accept multiple calls and forward them as one Kernel UserOp or one EIP-5792 sendCalls envelope. - useCGTokenWrite probes isApprovedForAll(owner, smartAccount) before sponsored transfers/burns. On first use, it signs the typed-data permit and batches the approval + actual call in one UserOp. Resolves the operator address synchronously via deriveKernelAddress to avoid racing useEffectiveAddress's useEffect-driven derivation. - smartAccount.ts swaps the shallow serializeUserOp helper for a BigInt-aware JSON.stringify replacer (catches nested bigints in the EIP-7702 authorization stub field that prepareUserOperation generates during gas estimation), and pins useMetaFactory: false because Arc Testnet's MetaFactory deployment skipped the post-deploy approveFactory step and reverts every call with 0xc88357cc. Tests: DEFAULT_INNER_DATA switched from the (now-pruned) donate(1) to setApprovalForAll(0, true). Stale "rejects batch execution" test replaced with four new cases covering accept/reject on inner-selector, inner-target, and empty-batch axes. Full suite: 240 passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ardcoded ETH CGOrganizationView's "Gas Sponsorship Balance:" row and the OrgGasSponsorshipBadge tooltip both hardcoded the suffix "ETH", which reads as "0 ETH" on Arc Testnet where the native gas token is USDC. Pull the symbol from targetNetwork.nativeCurrency, falling back to ETH for any chain whose definition omits it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Expands the original goals-only spec with the actual implementation: CGPaymaster, the Kernel-via-Pimlico and EIP-5792 smart-account paths, the /api/bundler and /api/paymaster routes, what selectors are sponsored (with rationale), what's intentionally not sponsored (admin ops, donations after the EOA-direct migration), the signed-approval bootstrap for first-time CGToken transfers, org gas-budget lifecycle, and known operational constraints (Reown chain coverage, EIP-7702 future path). Keeps the role-based framing from the original spec; corrects where reality diverged (org admin ops aren't sponsored — drain-prevention) and explains why. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ay's state
Drop self-referential framing ("original spec", "preserved", "where
reality diverged", commit hashes for historical context) and describe
the system as it works now: who is sponsored, who pays their own gas,
and why each path exists. Role sections (beneficiaries, donors, org
owners, registry owner) explain each role's current experience and
rationale without reference to earlier iterations. The donate/cancel/
refund subsection in §4 gets the same treatment.
Cross-reference fix: signed-approval bootstrap is §5 (was §6 in the
selector-table footnote).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three fixes that together let social/email-login users transact on chains (notably Arc Testnet) that Reown's blockchain-api proxy hasn't onboarded: 1. customRpcUrls — Reown's support steered us to AppKit's per-chain RPC override. Pass every targetNetwork's default HTTP RPC as a CustomRpcUrl[] entry so the embedded wallet (W3mFrame) hits the chain RPC directly instead of trying the proxy and rejecting on "We don't support the chainId". 2. metadata.icons — without it, Reown's modal fetches api.web3modal.com/public/getAssetImage/undefined → 404 + loopback CORS warning. Point at our own /logo.svg. 3. chainPublicClient() in smartAccount.ts — wagmi's usePublicClient gets wrapped by Reown's adapter when an embedded wallet is connected and routes some RPC through the wallet's restricted provider. Pimlico's getSenderAddress does a creation-style eth_call (data + no to) that the wallet rejects with "Requested RPC call is not allowed", which surfaces during Kernel address derivation. Build a raw viem publicClient bound to chain.rpcUrls.default and use that for toKernelSmartAccount, bypassing the wallet entirely. Same target URL, no interceptor. Also trim Reown modal: features.onramp / .swaps / .send all false — Chain.Giving funds users via the org's paymaster, the on-ramp auto- popup on zero balance was noise. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Encodes the active chain as the leading URL segment (/baseSepolia/programs, /arcTestnet/program/0x…) instead of relying on the wallet's chain or a global store. useTargetNetwork now reads useParams().network; useSelectedNetwork falls back to that instead of the (now-removed) zustand slice. A new useNetworkHref() wraps every in-app Link/router.push so navigation preserves the slug, and <ChainSync /> nudges the wallet to follow the URL (covers deep links and refreshes; NetworkOptions also fires switchChain on click for a snappier prompt). slugForChain keeps the first viem export key per chain id so collisions like baseSepoliaPreconf no longer shadow baseSepolia. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The [network] URL segment is the canonical active network, but the hand-written raw useReadContract/useReadContracts calls for the CG contracts omitted chainId, so wagmi defaulted them to the wallet's chain. Switching baseSepolia <-> arcTestnet flipped the display network instantly while reads kept hitting the wallet's (lagging or unswitched) chain, so the page showed the wrong network's data. - Add useNetworkRead: useReadContract/useReadContracts wrappers that inject chainId: targetNetwork.id (explicit chainId still overrides). - Swap the raw read imports across the program/org/token/wallet views, ProgramCard/ProgramRoleBadges, and useIsAdmin/useProgramOrganization/ useOrgGasSponsorship. - Flip the read-side `chainId ?? targetNetwork.id` patterns (native balance, donation-currency lookups) to targetNetwork.id; writes and wallet-capability lookups stay on the wallet chain. - On-network "Switch Network" now opens a NetworkOptions dropdown that rewrites the URL segment instead of RainbowKit's openChainModal, which only switched the wallet and left the URL (and thus the page) stale. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Picks up the new InvalidPaymasterData error and the updated postOp signature from the latest paymaster deployment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Multi-theme branch that lands the new paymaster, multi-chain support, gasless donations via permits, and a URL-driven network selector.
Paymaster / sponsored gas
setApprovalForAll+ paymaster batch validation for gasless flows.Networks
Donate
URL-driven network selection (final commit)
/[network]/…(e.g./baseSepolia/programs,/arcTestnet/program/0x…).useTargetNetworkreads the slug fromuseParams();useSelectedNetworkfalls back to it instead of a (now-removed) zustand slice.useNetworkHref()wraps every in-app Link / router.push so navigation preserves the slug.<ChainSync />(mounted in the providers) nudges the wallet to follow the URL — covers deep links, refreshes, and the network-switcher dropdown (which also firesswitchChainon click for a snappier prompt).slugForChainnow keeps the first viem export per chain id sobaseSepoliaPreconfno longer shadowsbaseSepolia.Build / infra
Test plan
yarn next:check-typesandyarn lintpass./baseSepolia/programsand/arcTestnet/program/0x…; the header network badge and balances match the URL, not the wallet chain./arcTestnet/...while wallet is on Base Sepolia —ChainSynctriggers a single switch prompt; user-rejected prompts don't re-fire on the same URL.🤖 Generated with Claude Code