Back to Projects

Filament Web3 Airdrop Platform

Token Distribution & Delegate Voting on an L2 Rollup

Overview

Role: Senior full-stack engineer in a team of 3–5: the Node server routes, the integration with the team's L2 rollup API, MetaMask signing of rollup transactions through a WASM serializer, the Ethereum wallet flows, and the campaign and voting web client. The rollup itself was built by teammates.

Full-stack work on a Web3 token-distribution platform: Node server routes with graceful degradation, the integration with the team's L2 rollup API, WASM-based transaction signing in MetaMask, Ethereum wallet flows, and the campaign and voting web client.

Key Features

  • Node server routes aggregating a blockchain-analytics API with per-widget graceful degradation (Promise.allSettled)
  • Integration with the team's L2 rollup REST API: campaign, vote and account queries, sequencer submission, ledger status
  • MetaMask signing of rollup transactions via the rollup's serializer compiled to WASM (EIP-191), lazy-loaded on first sign
  • BigInt-safe payloads (json-bigint) for 64-bit ids, nonces and vote weights; nonce 0 for wallets new to the rollup
  • Ethereum (Sepolia) wallet flows with ethers.js: connect, account and chain changes, network switching, delegate-registry reads
  • Campaign wizard, delegate voting and campaign pages; Clerk auth with PostgreSQL (Supabase)

Tech Stack

Backend & APIs

Node.jsTypeScriptREST APIPostgreSQL (Supabase)Clerk Auth

Blockchain

ethers.jsMetaMaskEIP-191 signingWebAssemblyL2 rollup APIEthereum (Sepolia)

Frontend & Tooling

TypeScriptTailwind CSSChart.jsStorybookVitestPlaywrightCloudflare

Challenges & Solutions

Signing Rollup Transactions With MetaMask

Problem

The rollup encodes transactions in its own binary format, but users only have MetaMask. Re-implementing that serialization in TypeScript would drift from the rollup's types with every protocol change.

Solution

Used the rollup's own serializer compiled to WebAssembly and published as an npm package with generated types. The client fetches the nonce, serializes the call in WASM, signs it with EIP-191 personal_sign and submits the signed transaction to the sequencer. The WASM module is lazy-loaded only when a user signs.

Silent Precision Loss Between the Rollup and JavaScript

Problem

Campaign ids, nonces and vote weights are 64-bit integers on the rollup side. JSON.stringify and JavaScript numbers round anything above 2^53 without an error.

Solution

Serialized call payloads with json-bigint and passed ids and chain ids as BigInt end to end, so large values reach the signer exactly as the rollup expects them.

The First Transaction From a New Wallet

Problem

A brand-new Ethereum address has no account on the rollup yet, so the account lookup returned 404 and the client could not build the user's first transaction.

Solution

Added a per-call error hook to the shared fetch wrapper that maps that 404 to nonce 0. Nonce plus chain id in the signed payload keep replay protection intact.

Dashboard Aggregation That Degrades Gracefully

Problem

The analytics dashboard needed several upstream calls per campaign to an external blockchain-analytics API; one slow or failing call used to break the whole page.

Solution

A Node route loads the organisation's campaigns, then fetches aggregations, interactions and daily change for each in parallel with Promise.allSettled at both levels. A failure nulls only that widget, and the route also returns status counts for the header.

Key Achievements

WASM Signing
Rollup transactions signed in MetaMask via the rollup's own serializer
Graceful Degradation
Node aggregation routes fail per widget, not per page
BigInt-Safe
64-bit ids, nonces and weights never rounded in JSON
Wallet Onboarding
New wallets can send their first rollup transaction
Filament Web3 Airdrop Platform - Project | Oleksandr Yusypenko