feat(add-money): offramp migration deposit entry#2330
Conversation
offramp.xyz is shutting down and migrating users to peanut. give offramp migrants a de-cluttered arbitrum deposit surface instead of the full multi-chain crypto picker: same rhino EVM SDA (funds land on the peanut arbitrum wallet), but stripped to a single arbitrum + usdc view with offramp copy. - gate a 'migrate from offramp' entry on the offramp badge (placeholder code, wired to the real badge once that PR lands) - deep-link /add-money/crypto?network=EVM&source=offramp drives a new 'offramp' variant of the crypto deposit view - tag completions as offramp_migration for the growth dashboard reuses the existing SDA creation, polling and success screen unchanged.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThe PR updates Add Money method selection to derive an Offramp badge flag from authenticated user data and conditionally render an extra migration option. It also changes invite campaign resolution to map explicit campaign parameters through the badge map before the existing fallback order. ChangesAdd Money method selection
Invite campaign resolution
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Comment |
Code-analysis diffPainscore total: 5858.65 → 5860.51 (+1.86) 🆕 New findings (12)
✅ Resolved (11)
📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
badge PRs (peanut-api-ts#1105, peanut-ui#2331) landed the link-granted OFFRAMP_USER badge; wire the deposit-entry gate to it.
?campaign=offramp (and any UTM-mapped vanity tag passed via the explicit campaign/campaignTag param) reached /badge/award raw and 400'd, since the backend matches the badge code. Resolve it through the UTM map first so the offramp migration link grants OFFRAMP_USER regardless of link shape. depends on peanut-ui#2331 (adds the offramp UTM map entry) reaching this base.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/Invites/InvitesPage.tsx`:
- Around line 40-41: The campaign mapping in InvitesPage.tsx falls through for
?campaign=offramp, so add an offramp entry to UTM_CAMPAIGN_TO_BADGE_MAP and
ensure the InvitesPage campaign normalization uses that mapped badge code
instead of passing the raw value through. Use the UTM_CAMPAIGN_TO_BADGE_MAP
constant and the campaign selection logic in InvitesPage to locate the fix.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3d9ced66-ab00-4467-8a91-873f4fe0c4db
📒 Files selected for processing (1)
src/components/Invites/InvitesPage.tsx
| const campaign = | ||
| (campaignParam && UTM_CAMPAIGN_TO_BADGE_MAP[campaignParam.toLowerCase()]) || |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Confirm an 'offramp' entry exists in UTM_CAMPAIGN_TO_BADGE_MAP.
rg -n -A2 "offramp" src/components/Invites/campaign-maps.tsRepository: peanutprotocol/peanut-ui
Length of output: 162
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the relevant files and inspect the campaign map plus invite page usage.
git ls-files | rg 'src/components/Invites/(InvitesPage\.tsx|campaign-maps\.ts)$'
echo '--- campaign-maps outline ---'
ast-grep outline src/components/Invites/campaign-maps.ts --view expanded || true
echo '--- InvitesPage outline ---'
ast-grep outline src/components/Invites/InvitesPage.tsx --view expanded || true
echo '--- campaign-maps excerpt ---'
nl -ba src/components/Invites/campaign-maps.ts | sed -n '1,220p'
echo '--- InvitesPage excerpt ---'
nl -ba src/components/Invites/InvitesPage.tsx | sed -n '1,220p'Repository: peanutprotocol/peanut-ui
Length of output: 615
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for any offramp-related mappings or alternate invite campaign maps.
rg -n "offramp|UTM_CAMPAIGN_TO_BADGE_MAP|campaignParam" src/components src | sed -n '1,220p'Repository: peanutprotocol/peanut-ui
Length of output: 25046
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- campaign-maps.ts ---'
sed -n '1,220p' src/components/Invites/campaign-maps.ts
echo '--- InvitesPage.tsx ---'
sed -n '1,120p' src/components/Invites/InvitesPage.tsxRepository: peanutprotocol/peanut-ui
Length of output: 10014
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
for path in [
Path("src/components/Invites/campaign-maps.ts"),
Path("src/components/Invites/InvitesPage.tsx"),
]:
print(f"=== {path} ===")
text = path.read_text()
for i, line in enumerate(text.splitlines(), 1):
if "offramp" in line.lower() or "UTM_CAMPAIGN_TO_BADGE_MAP" in line or "campaignParam" in line:
start = max(1, i-4)
end = min(len(text.splitlines()), i+6)
for j in range(start, end+1):
print(f"{j:4d}: {text.splitlines()[j-1]}")
print()
PYRepository: peanutprotocol/peanut-ui
Length of output: 5461
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
for path in [
Path("src/components/Invites/campaign-maps.ts"),
Path("src/components/Invites/InvitesPage.tsx"),
]:
print(f"=== {path} ===")
lines = path.read_text().splitlines()
for i, line in enumerate(lines, 1):
if "offramp" in line.lower() or "UTM_CAMPAIGN_TO_BADGE_MAP" in line or "campaignParam" in line:
start = max(1, i-4)
end = min(len(lines), i+6)
for j in range(start, end+1):
print(f"{j:4d}: {lines[j-1]}")
print()
PYRepository: peanutprotocol/peanut-ui
Length of output: 5461
Add an offramp entry to UTM_CAMPAIGN_TO_BADGE_MAP
?campaign=offramp still falls through to the raw value, so the invite request hits the backend as an invalid badge code and returns 400.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/Invites/InvitesPage.tsx` around lines 40 - 41, The campaign
mapping in InvitesPage.tsx falls through for ?campaign=offramp, so add an
offramp entry to UTM_CAMPAIGN_TO_BADGE_MAP and ensure the InvitesPage campaign
normalization uses that mapped badge code instead of passing the raw value
through. Use the UTM_CAMPAIGN_TO_BADGE_MAP constant and the campaign selection
logic in InvitesPage to locate the fix.
Why
offramp.xyz is shutting down and migrating its users to Peanut (~50k accounts, ~5k active). To improve conversion of migrants, Konrad asked for a dedicated, de-cluttered deposit entry that hands offramp users an Arbitrum address without the full multi-chain crypto picker.
Offramp users hold funds in ZeroDev smart accounts on Arbitrum, so the existing crypto deposit already does the job functionally (Rhino EVM SDA → funds land on the Peanut Arbitrum wallet). This PR is a thin, branded, tracked wrapper over that same infra — no backend changes.
What
/add-money/crypto?network=EVM&source=offramp.offrampvariant ofCryptoDeposit.view— same Rhino SDA, QR, address, copy, polling and success screen, but strips the chain fluff: single Arbitrum network chip + single USDC token chip, offramp copy, no network drawer / no supported-networks modal / no multi-chain tooltip.method_type: 'offramp_migration'for the growth dashboard.Draft — pending badge wiring⚠️
The offramp badge doesn't exist yet (Konrad's badges PR is WIP). This PR uses a placeholder badge code:
Before merge:
OFFRAMP_BADGE_CODEfor the real code from the badges PR.link-granted at signup (via the offramp signup invite code/campaign), notautoon an action — otherwise it's circular (need a deposit to earn the badge that unlocks the deposit entry).Reuse / scope
Reuses
useCryptoDepositPolling, the SDA creation query, andPaymentSuccessViewunchanged. Default (non-offramp) crypto deposit is untouched. FE-only.Test plan
/add-money/crypto?network=EVM&source=offramp→ see stripped Arbitrum + USDC surface