Here is your project brief reorganized into a clear, actionable task list, ensuring all technical requirements and deliverables are captured.
Your objective is to build a multi-wallet frontend application integrated with a deployed smart contract and real-time event tracking on the Stellar testnet. Selected monthly winners will receive a $10 prize based on the quality of their submission.
- Integrate Wallets: Implement
StellarWalletsKitto support multiple wallet connection options in your frontend. - Deploy Smart Contract: Deploy a smart contract to the Stellar testnet that supports reading and writing data.
- Connect the Frontend: Successfully call your deployed contract functions directly from the user interface.
- Handle Errors: Implement error handling for at least three specific scenarios: wallet not found, connection rejected by the user, and insufficient balance.
- Track Real-Time State: Implement event listening to synchronize contract state and display live transaction statuses (pending, success, fail).
Choose one of the concepts below, or propose your own custom idea to a DevRel team member:
| Project Concept | Core Feature |
|---|---|
| Token Swap Interface | Basic swap UI using the Stellar DEX orderbook. |
| NFT Minter | Mint simple NFTs with metadata and live status updates. |
| Crowdfunding Page | Collect donations and visualize real-time progress. |
| Real-time Auction | Enable live bidding with instant event updates. |
| Token Leaderboard | Track and display token holders dynamically. |
| Activity / Payment Feed | Stream contract events or multi-address payment statuses. |
| Live Poll | A single-question poll that tallies results in real-time. |
- Version Control: Maintain a public GitHub repository with a minimum of 10 meaningful commits to show your development progress.
- README Documentation: You must include local setup instructions, a screenshot showing the wallet connection options, your deployed contract address, and a verifiable transaction hash from the Stellar Explorer.
- Final Submission: Submit your GitHub repository link before the monthly deadline. An optional live demo link (e.g., Vercel, Netlify) is highly recommended.Given your escrow-network direction, Crowdfunding Page is the right pick here too — it's the closest approved concept to your bigger idea and it's a clean level-2 scope on its own. Here's a proper execution plan.
Files Changed:
contracts/Cargo.tomlcontracts/contracts/crowdfund/Cargo.tomlcontracts/contracts/crowdfund/src/lib.rs
Goal: Working, deployed contract before touching any UI.
- Scaffold a Soroban (Stellar smart contract) project
- Define contract state:
campaign_goal,total_raised,donations: Map<Address, i128>,deadline,status (active/completed/failed) - Write functions:
create_campaign(goal, deadline)donate(amount)— writes to state, emits eventget_campaign_state()— read-onlywithdraw()(only if goal met + creator-only)
- Deploy to Stellar testnet
- Make one test donation via CLI, confirm state updates
- Commit checkpoint: "Deploy initial crowdfunding contract to testnet"
- Contract Address:
CAKBK6LDUAYFCIGDMGWGYEXDSRSVCLDJDUXHOSCS2BQYBNZLS3NPFRQS - Tx Hash:
93e4b3b2fc78ef5bfd1bf4cd31364c0f1c1cf63cb3164767c9c547780957168a
- Contract Address:
Files Changed:
src/context/StellarContext.tsxsrc/pages/DashboardPage.tsxpackage.json
Goal: Multi-wallet connect working before contract calls.
- Install and configure
StellarWalletsKit - Support at least 2–3 wallet options (Freighter, Albedo, etc. — whatever the kit exposes)
- Build the connect UI (this is the screenshot you need for submission)
- Implement the 3 required error cases now, not later:
- Wallet not found → clear message + link to install
- Connection rejected → toast/banner, don't crash the app
- Insufficient balance → check before submitting tx, show inline error
- Commit checkpoint: "Add multi-wallet connection with error handling"
Files Changed:
src/context/StellarContext.tsxsrc/pages/DashboardPage.tsx
Goal: UI actually calls your deployed contract.
- Build the donation form (amount input → wallet signs → calls
donate()) - Build the progress display (raised / goal, percentage bar)
- Wire
get_campaign_state()on page load to populate initial state - Handle transaction lifecycle UI states explicitly: pending → success/fail (this is a named requirement, don't skip it — show a spinner then a checkmark/error icon, not just a silent refresh)
- Commit checkpoint: "Connect frontend donation flow to contract"
Files Changed:
src/context/StellarContext.tsxsrc/pages/DashboardPage.tsx
Goal: State updates live without manual refresh.
- Set up event listening (poll the contract or subscribe via Horizon/RPC, depending on what Soroban tooling supports for your setup)
- When any donation event fires, update the progress bar for all connected clients, not just the donor's own screen
- Add a small live activity list ("0x1234... donated 50 XLM — 2 min ago") — cheap to build, strong demo value, and doubles as your "Activity Feed" secondary concept from the table
- Commit checkpoint: "Add real-time event listening for live progress updates"
Files Changed:
-
README.md -
vercel.json -
Write the README with:
- Local setup instructions (clone, install, env vars, run)
- Screenshot of wallet connection UI
- Deployed contract address
- A verifiable tx hash link to Stellar Explorer (testnet)
-
Verify you have 10+ meaningful commits — if you've been committing at each checkpoint above you're already close; add a couple more for styling/bugfixes if short
-
Deploy frontend to Vercel/Netlify (optional but recommended — do it, it's low effort and clearly boosts submission quality)
-
Final pass: test the full flow on a clean browser profile (no cached wallet state) to make sure a judge with zero context can actually use it
| # | Commit message |
|---|---|
| 1 | Scaffold project structure |
| 2 | Add crowdfunding contract skeleton |
| 3 | Implement donate() and state read functions |
| 4 | Deploy contract to testnet |
| 5 | Add StellarWalletsKit integration |
| 6 | Add wallet error handling (not found / rejected / balance) |
| 7 | Build donation UI form |
| 8 | Wire frontend to contract calls |
| 9 | Add pending/success/fail transaction states |
| 10 | Implement real-time event listener |
| 11 | Add live activity feed |
| 12 | Write README + add screenshots |
That's 12 natural checkpoints from real work — no filler commits needed if you follow the phases in order.
Solid execution on Level 2 — full checklist done, clean contract address and hash ready. Level 3 is a real step up: it wants production dApp practices, not just "does it work." Here's the plan, built directly on top of your crowdfunding contract.
Add a second contract that talks to your existing crowdfunding contract — this single decision satisfies the inter-contract communication requirement naturally instead of bolting it on artificially.
Recommended addition: a RewardBadge contract. When a donor's cumulative donation crosses a threshold (e.g., 100 XLM total), the crowdfunding contract calls into the badge contract to mint/record a "Top Supporter" badge. This gives you:
- Genuine inter-contract communication (crowdfunding → badge contract)
- A natural second event stream to track (badge-earned events, separate from donation events)
- Zero wasted work from Level 2 — your existing contract just gets a new call inside
donate()
Files Changed:
contracts/contracts/reward_badge/Cargo.tomlcontracts/contracts/reward_badge/src/lib.rscontracts/contracts/crowdfund/src/lib.rssrc/context/StellarContext.tsxREADME.md
Goal: Two contracts, talking to each other, deployed and verified.
- Design the
RewardBadgecontract:award_badge(donor_address, tier),get_badges(donor_address) - Deploy
RewardBadgeto testnet independently first, verify it works standalone - Modify
CrowdfundingContract::donate()to callRewardBadge::award_badge()when a donor crosses the threshold (this is your inter-contract call — use the contract's Address as a cross-contract client) - Handle the failure case: what happens if the badge-contract call fails but the donation itself succeeded? (Decide and document: donation should NOT roll back — badge award is best-effort, log the failure)
- Redeploy updated crowdfunding contract, save new address + tx hash
- Commit checkpoint: "Add RewardBadge contract with inter-contract calls from donate()"
Files Changed:
contracts/contracts/crowdfund/src/lib.rscontracts/contracts/crowdfund/src/test.rspackage.jsonvite.config.jssrc/pages/Frontend.test.tsx
Goal: 3+ passing tests, visible in a screenshot.
Contract tests (Rust/Soroban test framework):
- Test:
donate()correctly updatestotal_raisedanddonationsmap - Test:
donate()past the threshold triggers a badge award (mock or check cross-contract state) - Test:
withdraw()fails if called by non-creator or before goal is met - Test: donation event is emitted with correct payload (covered equivalent coverage with other tests)
Frontend tests (Jest/Vitest + React Testing Library, or similar):
-
Test: wallet-not-found error renders the correct message
-
Test: donation form rejects a submission with insufficient balance
-
Test: progress bar renders correct percentage given mock contract state
-
Run full test suite locally, screenshot the passing output (this is a required submission asset — capture it now)
-
Commit checkpoint: "Add contract and frontend test suites"
Goal: Make both contracts' info directly visible in the UI, not just buried in the README.
Files Changed / Created:
src/components/ContractInfoPanel.tsx(NEW — component for contract transparency, copy buttons, explorer links, and live ledger polling)src/config/contracts.ts(NEW — centralizes both contract addresses, verified tx hashes, explorer URL helpers)src/config.ts(re-exports centralized contracts configuration)src/pages/LandingPage.tsx(integrated ContractInfoPanel above footer)src/pages/DashboardPage.tsx(integrated ContractInfoPanel and cleaned up linter warnings)src/components/Toast.tsx(added eslint-disable for fast-refresh)src/pages/Frontend.test.tsx(added ContractInfoPanel rendering and expansion unit tests)
Tasks:
- Build a collapsible/footer panel showing:
- Crowdfunding contract address (truncated, with copy-to-clipboard)
- RewardBadge contract address (truncated, with copy-to-clipboard)
- Network label ("Stellar Testnet")
- Direct links to both contracts on Stellar Expert / Explorer
- Last-verified deployment tx hash for each, also linked to Explorer
- Place this panel where a reviewer will actually find it — a footer section or an "ℹ️ Contract Info" expandable card near the top, not hidden in a settings menu
- Optional nice touch: show live-fetched contract metadata (e.g., last event timestamp / ledger sequence) to prove it's not just static text
- Commit checkpoint: "Add contract transparency panel with addresses and explorer links"
This directly serves two things at once: your ask for visibility, and it's genuinely good practice for any real crowdfunding dApp — donors should always be able to verify what they're sending money to.
Goal: Automated pipeline running on every push, screenshot of it passing — required submission asset.
Files Changed / Created:
.github/workflows/ci.yml(multi-stage GitHub Actions pipeline with Rust 1.81.0, wasm32 target, Cargo caching, contract test, frontend test, lint, and build)
Tasks:
- Pipeline stages: install deps → build Soroban contract → run contract tests → run frontend tests → lint/typecheck → build frontend for production
- Push a commit, confirm pipeline runs green end-to-end
- Screenshot the passing GitHub Actions run
- Commit checkpoint: "Add CI pipeline with build, lint, and test stages"
Goal: The componentized dashboard actually adapts to phone screens — required submission asset (mobile screenshot).
Files Changed / Created:
src/components/Navbar.tsx(mobile responsive padding and touch targets)src/components/Toast.tsx(responsive positioning and container constraints for narrow mobile viewports)src/pages/DashboardPage.tsx(mobile stacking order: Donate Form first, mobile wallet badge, 48px touch targets, trimmed feed)
Tasks:
- Apply the mobile stacking order from the UI plan (donate → progress → badges → feed → my transactions)
- Fix tap target sizes, wallet modal overflow, and text truncation at narrow widths (down to 320px)
- Re-verify all 3 error cases render cleanly at mobile width (don't let toasts clip off-screen)
- Trim activity feed and format responsive wrapping on mobile
- Screenshot mobile UI (real device or dev-tools emulation)
- Commit checkpoint: "Mobile responsive layout and loading/error state polish"
Goal: Code that reads as built-to-last, not built-to-demo.
Files Changed / Created:
.env.example,.env(centralized environment configurations)src/config/contracts.ts(centralized contract addresses & verified tx hashes)src/hooks/useCrowdfundingContract.ts(extracted contract methods, TxStatus state machine, and isSubmitting click-guard with finally guarantee)src/context/StellarContext.tsx(cursor pagination with lastCheckedLedger + 1, scoped exponential backoff on read polling, isMounted cancellation)src/pages/DashboardPage.tsx(client-side input validation rejecting <= 0 amounts, balance checks, click-guard)src/pages/Frontend.test.tsx(unit tests for input validation, click-guard, and mobile components)
Tasks:
- Move CONTRACT_ID, HORIZON_URL, RPC URL, network passphrase into .env — never hardcoded; document required vars in README
- Fix the polling bugs from earlier: cursor-based pagination on getEvents, lastCheckedLedger + 1, isMounted guard on setCampaign
- Extract contract-interaction logic (fetchCampaignState, donate, event polling) into useCrowdfundingContract.ts — separates concerns and makes it independently testable
- Add client-side input validation on DonateForm (reject ≤0 amounts, disable submit if amount > balance) before any transaction is built
- Add a donate-button click-guard (disable while a transaction is in-flight) to prevent double-submission from a double-click
- Commit checkpoint: "Production hardening: env config, input validation, retry logic, hook extraction"
(unchanged — env config, polling bugfixes, hook extraction, input validation, click-guard)
If pursuing Option B (milestone bridge), insert here:
- Add a
milestones: Vec<Milestone>field to campaign state (simple:{description, amount, released: bool}) -
withdraw()becomesrelease_milestone(index)— creator can only release one milestone's worth at a time, up tototal_raised - Frontend: show milestone list with released/pending status in
CampaignProgress - Document explicitly in README: "This is a simplified proof-of-concept for the milestone-gated escrow pattern from my larger Freelancer Escrow Network design — see [link/spec] for the full version."
Files Changed / Created:
README.md(comprehensive architecture, inter-contract tradeoff rationale, contract transparency panel, test instructions, and Freelancer Escrow Network relationship narrative)
Tasks:
- Add a short "Relationship to Freelancer Escrow Network" section in the README — 3–4 sentences explaining that this crowdfunding dApp is a scoped implementation of the trust-and-transparency pattern from your larger escrow project, built within this challenge's approved-concepts constraint.
- Document cross-contract error handling rationale (best-effort badge awards preserving core financial pledges).
- Document On-Chain Contract Transparency Panel and verified testnet transaction proofs.
- Document CI/CD pipeline, local setup, and test execution procedures.
- Add demo video link / final asset recording.
- Your bounty submission is on track and doesn't need the escrow network merged in to succeed — don't let scope creep threaten your deadline.
- I've added the contract-visibility panel as its own phase since you specifically flagged it, and it's a clean win either way.
- The Freelancer Escrow Network stays alive as your bigger resume story, referenced honestly in the README rather than falsely implied by the code.
| # | Commit message |
|---|---|
| 13 | Add RewardBadge contract |
| 14 | Wire inter-contract call from donate() to award_badge() |
| 15 | Handle badge-call failure without rolling back donation |
| 16 | Add contract test suite |
| 17 | Add frontend test suite |
| 18 | Set up GitHub Actions CI pipeline |
| 19 | Fix mobile responsive layout issues |
| 20 | Add loading states across contract-call flows |
| 21 | Move config to environment variables |
| 22 | Add contract-interaction service layer |
| 23 | Add donation click-guard (idempotency) |
| 24 | Update README with architecture + testing docs |
| 25 | Add demo video link |
| Requirement | Covered by |
|---|---|
| Inter-contract communication | Phase 6 |
| Event streaming & real-time updates | Level 2 base + Phase 6 badge events |
| CI/CD pipeline | Phase 8 |
| Deployment workflow | Phase 6 + 8 (documented in README) |
| Mobile responsive frontend | Phase 9 |
| Error handling & loading states | Level 2 base + Phase 9 |
| Tests (contract + frontend) | Phase 7 |
| Production-ready architecture | Phase 10 |
| Documentation & demo | Phase 11 |
One risk to flag: the cross-contract failure-handling decision in Phase 6 (should a failed badge-award roll back the donation?) is exactly the kind of design tradeoff judges/reviewers like seeing explicitly reasoned about — write that decision and reasoning directly into your README, not just in code comments. It's cheap to add and reads as senior-level thinking.
Given everything already built (wallet context, campaign state, donations, badges, activity feed, error/loading states), here's a concrete dashboard plan — not just aesthetics, but a layout that makes the judge's checklist visibly obvious the moment the page loads.
┌─────────────────────────────────────────────────────────┐
│ Header: Logo/Title [Wallet: 0x1a2b...] [•●●] │ ← connect status + balance
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────┐ ┌──────────────────────┐ │
│ │ CAMPAIGN PROGRESS │ │ YOUR BADGES │ │
│ │ ━━━━━━━━━━━━━━━░░░ 72%│ │ 🏅 Top Supporter │ │
│ │ 3,600 / 5,000 XLM │ │ (or "None yet") │ │
│ │ Deadline: 12 days left │ │ │ │
│ └─────────────────────────┘ └──────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐│
│ │ DONATE ││
│ │ [ amount input ] XLM [ Donate Button ] ││
│ │ ⚠ inline validation errors appear here ││
│ └─────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────┐│
│ │ LIVE ACTIVITY FEED ● live ││
│ │ GABC...9F2 donated 50 XLM 2 min ago ││
│ │ GDEF...3A1 donated 100 XLM 🏅 5 min ago ││
│ │ GHIJ...7B4 donated 25 XLM 8 min ago ││
│ └─────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────┘
1. Header — wallet state is always visible
- Not connected: "Connect Wallet" button
- Connecting: spinner + "Connecting..."
- Connected: truncated address + balance badge (
GABC...9F2 · 245 XLM) - This single element needs to visibly handle all 3 error states from Phase 2 — put the error banner directly under the header, not buried in a modal, so a reviewer scanning the page sees it immediately
2. Campaign Progress card
- Big number first (percentage or amount raised), progress bar second — the number is what people scan for
- Deadline as a countdown, not a static date ("12 days left" reads better live than "March 15")
- Status badge (Active / Goal Met / Ended) as a colored pill — cheap visual signal for campaign state from your contract
3. Badges card — makes Phase 6's inter-contract work visible in the UI This is important: your RewardBadge contract is your Level 3 differentiator, so don't bury it in the activity feed only — give it its own card so a reviewer immediately sees "oh, there are two contracts talking to each other" without reading code.
4. Donate form
- Amount input with live validation (disable button if amount > balance, show inline error before submission — this is your Phase 10 idempotency/validation work made visible)
- Button states:
Donate→Confirm in Wallet...→Pending...→Success ✓/Failed ✕ - This state sequence is literally the "pending/success/fail" requirement from your brief — make each state visually distinct (color + icon), not just text swaps
5. Live Activity Feed
- A pulsing "● live" indicator next to the title — small detail, but visually communicates "this updates in real time" without the reviewer having to wait and watch
- Each row: truncated address, amount, relative time ("2 min ago", not a timestamp), and a small badge icon if that donation triggered a reward
- New items should animate in at the top (a simple fade/slide-in on new entries makes "real-time" feel real, not just technically true)
Stack everything vertically, single column, in this priority order:
- Wallet connect (header, collapsed/simplified)
- Donate form (most important action — put it near the top on mobile, not buried below progress)
- Campaign progress
- Badges
- Activity feed (can be shorter — 5 items instead of 10)
┌──────────────────┐
│ Header (wallet) │
├──────────────────┤
│ Donate form │ ← primary action first on mobile
├──────────────────┤
│ Progress card │
├──────────────────┤
│ Badges │
├──────────────────┤
│ Activity feed │
└──────────────────┘
Reasoning: on desktop you have room to lead with information (progress); on mobile, screen real estate is precious and the action (donating) should be reachable without scrolling past decorative content.
- One accent color for "money in motion" — use it consistently for the progress bar fill, the donate button, and the pulsing live indicator, so the eye learns "this color = active/live" across the whole page
- Skeleton loaders, not blank space — on initial load, show a skeleton for the progress card and activity feed while
get_campaign_state()resolves, instead of a flash of empty/zero values - Toast notifications for transient states — wallet-rejected, donation-success, badge-earned — toasts keep these from disrupting layout, and they're an easy, professional-looking win
- Monospace font for addresses and amounts — small detail, but it's what makes a crypto-native UI look intentional rather than like a generic form was reskinned
- Don't put the badge contract's data inside a settings/detail page — it needs to be front-and-center since it's your inter-contract-communication proof point for Level 3
- Don't use a spinner with no label — every loading state should say what it's waiting on ("Confirming on network...", not just a blank spinner), since a reviewer testing pending/fail states needs to understand what they're looking at without reading your code
If you want, I can turn this into an actual React + Tailwind component structure (file breakdown: Header.tsx, CampaignProgress.tsx, BadgesCard.tsx, DonateForm.tsx, ActivityFeed.tsx) that plugs directly into your existing StellarContext.