feat: multi-chain sign-in — Substrate + Ethereum + Solana#84
Merged
Conversation
Phase A — server verifier abstraction: - Add server/api/auth/ with a per-chain verifier registry, shared statement validation, payload parsing, and address normalization. - Refactor auth.middleware.js to one chain-aware pipeline. The x-siws-auth payload gains an optional `chain` field defaulting to 'substrate', so existing SIWS clients are unaffected. Phase B — Ethereum (SIWE / EIP-4361): - Add an Ethereum verifier (viem parseSiweMessage + offline recoverMessageAddress); rejects expired / not-yet-valid messages. - Chain-tagged AUTHORIZED_SIGNERS via authorizedSigners.js (`chain:address` or bare => substrate); admins authorized per chain. - Client: client/src/lib/auth/ wallet-provider abstraction (Substrate + Ethereum/EIP-6963), useWalletAuth hook, and a ChainPicker component. - AdminPage and ProjectDetailsPage migrated to useWalletAuth; WalletConnectionBanner gains the chain picker. - addressUtils / constants made chain-aware (backward compatible). Server: 153 tests pass. Client build + lint green.
- Add a Solana (Sign-In With Solana) verifier: ed25519 signature verification via tweetnacl, base58 address handling via bs58, and a parser for the fixed SIWS-style message format. Rejects expired messages. - normalize.js gains a solana case (canonical base58 of the 32-byte public key); the verifier registry now includes solana. - Client: solanaProvider (window.solana / Phantom) builds and signs the matching message format; registry + ChainPicker now offer Solana. - addressUtils normalizes solana addresses for chain-aware comparison. Server: 158 tests pass. Client build + lint green.
Add a `chain` discriminator to every address-bearing table so team members and payout addresses can be Substrate, Ethereum or Solana. - Migration 20260520000000: team_members.wallet_chain, projects.donation_chain (both with a CHECK constraint), and a composite (wallet_chain, wallet_address) primary key on wallet_contacts. Existing rows backfill to 'substrate' via the column default before any constraint/PK is applied. - validation.js: validateAddress(chain, address) with per-chain format rules; validateTeamMember / validateProject validate the (chain, address) pair. - project.repository: transform + insert walletChain / donationChain; findByTeamWallet filters on (wallet_chain, wallet_address). - updatePayoutAddress accepts donationChain and validates per chain. - wallet_contacts repo/service/controller are chain-aware; requireOwnWallet exposes the signer's chain on req.user. Server: 161 tests pass.
Wire the client API layer to the multi-chain server endpoints: - updateTeam / updatePayoutAddress forward donationChain and per-member walletChain (both default to 'substrate'). - getWalletContact accepts an optional chain and sends ?chain=. Client build + lint green.
- TeamPaymentSection: a chain selector on each team-member wallet and on the payout address; read-only views show a chain badge. handleTeamPaymentSave forwards walletChain / donationChain through api.updateTeam. - ProjectDetailsPage: ApiProject / ApiTeamMember carry the chain fields; donationChain is passed to TeamPaymentSection. - NotificationsCard: signs via useWalletAuth (multi-chain) instead of an inline substrate-only SIWS block; getWalletContact passes the connected chain. Client build + lint green.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced May 19, 2026
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
Adds multi-chain sign-in to Stadium. Anyone — admins and team members — can authenticate with the wallet ecosystem they use: Substrate (SIWS), Ethereum (SIWE / EIP-4361), or Solana (Sign-In With Solana). Team-member and payout addresses become multi-chain too.
Backward-compatible throughout: the
x-siws-authpayload gains an optionalchainfield defaulting tosubstrate, and every new DB column / address field defaults tosubstrate, so existing flows are unchanged.Shipped in four phases (one logical commit group each):
server/api/auth/): per-chain verifier registry, shared statement validation, payload parsing, address normalization.auth.middleware.jsrefactored to one chain-aware pipeline.parseSiweMessage+ offlinerecoverMessageAddress), chain-taggedAUTHORIZED_SIGNERS, client wallet-provider abstraction (client/src/lib/auth/), EIP-6963 discovery,useWalletAuthhook,ChainPicker.AdminPage+ProjectDetailsPagemigrated off the Polkadot-only wallet code.window.solanaprovider.team_members.wallet_chain,projects.donation_chain, and a composite PK onwallet_contacts; repository / validation /wallet_contactsAPI are chain-aware;TeamPaymentSectiongains chain selectors on team wallets and the payout address.Test plan
npm test— 161 tests pass (21 files), including new verifier /authorizedSigners/parsePayload/statementssuites and updatedauth.middleware+wallet-contactsuites.npm run build(tsc) — green.npm run lint(--max-warnings 0) — green.supabase/migrations/20260520000000_multichain_addresses.sqlto Supabase perdocs/PRODUCTION_DEPLOYMENT.md. Thewallet_contactscomposite-PK swap is the highest-risk DDL — run on a Supabase branch first. All existing rows backfill tosubstratevia column defaults before any constraint is applied.ethereum:0x…/solana:…entries toAUTHORIZED_SIGNERS(server) andVITE_ADMIN_ADDRESSES(client).Notes / follow-ups
TeamAddressList,ConfirmPaymentModal) render multi-chain addresses but don't yet badge the chain. Legacy unused modals (UpdatePayoutModal,EditTeamModal,UpdateTeamModal) were left as-is.expirationTime(10-min) as partial mitigation; a server-side nonce store is recommended as a follow-up.🤖 Generated with Claude Code