Overview
The W9 Compliance system tracks user earnings from the SFLUV faucet (admin wallet) and requires W9 tax form submission when annual earnings exceed $600. This ensures compliance with IRS 1099-MISC reporting requirements. File Reference:backend/handlers/w9.go:21
Key Concepts
Earnings Threshold
The system monitors payments from designated admin addresses:- Threshold: $600 USD equivalent in SFLUV tokens per calendar year
- Admin Addresses: Configured via
PAID_ADMIN_ADDRESSESenvironment variable - Tracking: Automatic via blockchain indexer (Ponder)
backend/handlers/w9.go:31-42, 66-70
W9 Requirement States
- Below Threshold - No W9 required, transactions allowed
- Threshold Reached - W9 required, marked in database, admin notified
- W9 Pending - Submitted but awaiting admin approval
- W9 Approved - Approved by admin, restriction lifted
- W9 Rejected - Rejected by admin, user must resubmit
backend/handlers/w9.go:122-163
Transaction Flow
Pre-Transaction Compliance Check
Before any transfer from faucet:- Check Sender - Is transaction from admin address?
- Calculate Total - Sum all prior earnings this year
- Calculate New Total - Add proposed transaction amount
- Check Threshold - Will new total exceed $600?
- Check W9 Status - Is W9 submitted and approved?
- Return Decision - Allow or block transaction
POST /w9/check-compliance
Code Reference: backend/handlers/w9.go:572-627
Request Format
backend/handlers/w9.go:586-594
Response Format
Allowed:backend/handlers/w9.go:137-163
Post-Transaction Recording
After successful blockchain transaction:- Record Transfer - Save transaction details
- Update Earnings - Recalculate year-to-date total
- Check Threshold - Has user now crossed $600?
- Mark W9 Required - If threshold crossed
- Notify Admin - Email admin about W9 requirement
POST /w9/record-transaction
Code Reference: backend/handlers/w9.go:629-662
Request Format
backend/handlers/w9.go:642-647
W9 Submission
Submission Methods
1. Direct API Submission
Endpoint:POST /w9/submit
Request:
backend/handlers/w9.go:299-320
2. WordPress Webhook
External W9 form submissions via webhook: Endpoint:POST /w9/webhook
Authentication: Optional X-W9-Secret or X-W9-Key header matching W9_WEBHOOK_SECRET env var
Request (JSON):
backend/handlers/w9.go:465-513
Submission Validation
- Wallet Address Required - Must be valid Berachain address
- Email Required - Must be valid email format
- Year Optional - Defaults to current year if not provided
- Duplicate Prevention - Cannot submit if already pending or approved for same year
backend/handlers/w9.go:515-556
Error Responses
Duplicate Pending:backend/handlers/w9.go:532-541
Admin Review
Pending Submissions
Endpoint:GET /w9/pending
Response:
backend/handlers/w9.go:322-361
Approving Submissions
Endpoint:POST /w9/approve
Request:
“Your W9 has been approved for wallet 0xuser… The $600 restriction has been removed.”Code Reference:
backend/handlers/w9.go:363-422
Rejecting Submissions
Endpoint:POST /w9/reject
Request:
backend/handlers/w9.go:424-462
Testing Flow
Local Testing with Anvil
FromTESTING.md:
1. Start Test Environment
- Anvil fork (Berachain)
- Backend server
- Ponder indexer
- Frontend
source/TESTING.md:16-32
2. Create Test Transfer
- Sends 200 SFLUV from faucet to new random wallet
- Waits for Ponder to index transfer
- Waits for
app.w9_wallet_earningsto update - Creates event + code
- Prints redeem URL showing “W9 Required” UI state
source/TESTING.md:34-44
3. Submit W9 (Mock)
source/TESTING.md:46-52
4. Approve W9
- Open Admin → W9 tab
- Find pending submission
- Click “Approve”
- Submission marked approved
source/TESTING.md:69-71
5. Verify Unblocked
"Unblocked: OK"
Reference: source/TESTING.md:72-77
Webhook Testing
Endpoint:POST /w9/webhook
JSON Payload:
source/TESTING.md:54-68
Database Schema
w9_wallet_earnings
backend/structs/bot.go (W9WalletEarning struct)
w9_submissions
backend/structs/bot.go (W9Submission struct)
Environment Variables
Required Configuration
backend/handlers/w9.go:31-33, 67, 160, 270-273
Integration Points
Ponder Indexer
Blockchain indexer watches for ERC20 transfer events:- Event Detection - Transfer from admin address
- Callback - POST to
/ponder/callback - W9 Processing -
ProcessPaidTransfer()called - Database Update - Earnings record updated
backend/handlers/w9.go:166-260
Frontend Integration
Frontend pages display W9 status: Merchant Status Page (/merchant-status):
- Shows current earnings
- Displays W9 requirement status
- Links to W9 submission form
- Shows pending/approved state
/unwrap):
- Checks W9 compliance before allowing unwrap
- Blocks if W9 required but not approved
Admin Notifications
When threshold is reached: Subject:W9 required for wallet {address}
Body:
W9_ADMIN_EMAIL via Mailgun.
Code Reference: backend/handlers/w9.go:262-297
User Notifications
When W9 is approved: Subject:W9 approved - restriction removed
Body:
backend/handlers/w9.go:391-409
Best Practices
For Admins
- Review Promptly - Process W9 submissions within 24-48 hours
- Verify Information - Ensure wallet address matches submission
- Clear Rejections - Provide specific reason when rejecting
- Annual Review - Check for new year threshold crossings in January
- Record Keeping - Maintain separate tax records for 1099 filing
For Developers
- Test Annually - Verify year rollover logic
- Monitor Thresholds - Alert if many users near $600
- Backup Submissions - Maintain copies of W9 forms
- Webhook Security - Always use
W9_WEBHOOK_SECRETin production - Error Logging - Log all W9 check failures for debugging
For Users
- Submit Early - Don’t wait until blocked to submit W9
- Accurate Email - Use email you regularly check
- Correct Wallet - Verify wallet address before submitting
- Monitor Earnings - Track your year-to-date total
- Annual Resubmit - W9 required each calendar year over $600
Troubleshooting
Common Issues
Transaction Blocked Despite Approved W9- Check submission year matches current year
- Verify wallet address exact match (case-insensitive)
- Confirm approval not rejected later
- Check database for
pending_approval=falseandapproved_atset
- Verify sender address in
PAID_ADMIN_ADDRESSES - Check Ponder indexer is running and synced
- Confirm
/ponder/callbackendpoint working - Verify
w9_wallet_earningstable updates
- Check
X-W9-Secretheader matches env var - Verify JSON or form encoding matches expected format
- Check server logs for validation errors
- Confirm wallet address format valid
- Verify Mailgun API key and domain configured
- Check
W9_ADMIN_EMAILenvironment variable set - Review server logs for Mailgun errors
- Confirm email addresses valid format
Debug Queries
Security Considerations
Webhook Security
- Shared Secret - Use strong, random
W9_WEBHOOK_SECRET - HTTPS Only - Require TLS for webhook endpoint
- Rate Limiting - Prevent abuse of submission endpoint
- Input Validation - Sanitize all user inputs
Data Privacy
- PII Protection - Encrypt W9 submissions at rest
- Access Control - Limit admin access to W9 data
- Retention Policy - Define how long to keep submissions
- Audit Logging - Log all W9 approvals/rejections
Compliance
- IRS Requirements - Ensure W9 form matches IRS standards
- 1099 Filing - Integrate approved W9s with 1099 generation
- Year-End Reporting - Generate annual reports for tax filing
- Backup Verification - Manual review for high-value submissions