SocialPay EVVM: Send PYUSD to @telegram handles, claim to any wallet
So we have several problems in payments like:
and solution we built is: Pay anyone by @handle on Telegram Funds go direct if a wallet is linked; else held securely until claim EVVM-powered async nonces + EIP-712 signatures for gasless UX
EVVM-style async nonces + EIP-712 intents let a relayer submit gasless PYUSD payments to @handles. If a handle is linked, tokens transfer instantly; otherwise they accrue as a pending balance claimable by any wallet the recipient controls. Telegram bot handles UX and typed-data signing; a lightweight Next.js dApp handles claims. Smart contracts (Foundry, Solidity)
SocialPay core Verifies EIP-712 payment intents signed by the sender. Resolves a normalized @handle to a wallet if linked; otherwise credits a pending balance keyed by the handle hash. Claim flow lets the rightful handle owner route pending to any wallet. EVVM async nonces Monotonic, per-sender async nonces prevent replay across relayer submissions. Nonce verified on-chain; increment happens atomically with payment/claim. Name/Handle service Minimal on-chain registry for handle → wallet mapping. Handle stored as keccak256(normalize(lowercase(@handle))) to avoid Unicode/case traps. Tokens Works with standard ERC-20 (PYUSD). If Permit/Permit2 present, supports permit-style approvals; else requires prior allowance. Example typed data (PaymentIntent)
Telegram bot + relayer (TypeScript, Node.js)
Telegraf bot for commands: /wallet, /pay, /confirm, /balance. Signature builder composes domain + PaymentIntent typed data and asks the sender to confirm in-chat. Stateless relayer (Executor) holds a single key, submits payToHandleWithSignature and claimPending. Idempotent via async nonce. Ethers/viem for RPC; rate-limited, auto-retry with exponential backoff; EIP-1559 fee estimation. Claim web app (Next.js + Wagmi/Viem)
Minimal UI: enter @handle → fetch pending → connect wallet → sign → claim. Uses the same domain separator and struct encoding as the bot to avoid drift. Built with Wagmi/Viem, RainbowKit (or WalletConnect) for wallet UX. How pieces fit
User → Telegram bot: /pay @bob 10 Bot → builds EIP-712 intent with async nonce Relayer → submits payToHandleWithSignature to SocialPay Contract → resolve handle; if linked, transfer PYUSD; else credit pending Recipient → web dApp → claimPending to chosen wallet Security and safeguards
Replay protection: EVVM async nonces verified on-chain. Domain separation: chainId, verifying contract, name/version baked into EIP-712 domain. Reentrancy guards around token interactions; safe ERC-20 handling for non-standard returns. Handle normalization: lowercase, Unicode NFC, trimmed suffix, disallow confusables where possible. Least privilege: relayer never holds user funds; only forwards verified intents. Partner tech and why it helped
Telegram Bot API: familiar UI, zero-friction onboarding. PYUSD (ERC-20): stable unit of account for social payments. Etherscan/Blockscout: transaction transparency during demo. Alchemy/Infura RPC: stable Sepolia/Mainnet endpoints for relayer and dApp. WalletConnect/RainbowKit: quick multi-wallet support in claim flow. Notable hacky bits
Deterministic handle hashing: keccak256(normalize(handle)) makes mapping robust and cheap on-chain. Async nonce “queues”: off-chain we mirror the next expected nonce to avoid user confusion and relayer collisions. Telegram identity link: optional signed proof using Telegram Login Widget or one-time code DM; falls back to manual link + on-chain attestation. Gasless UX: when Permit2 available, combine permit + pay in one tx; else guide users to pre-approve PYUSD once. Tooling and DX
Foundry for build, fuzz, and invariant tests (nonce monotonicity, claim safety). TypeChain/ABIs shared across bot and web to prevent encoding drift. GitHub Actions for tests + size checks; .env templates for bot and web. Challenges
Handling non-standard ERC-20 return values safely. Unicode edge cases in @handles. Making relayer idempotent across network hiccups while keeping UX snappy.

