Skip to content

feat(swap-service): add affiliate address tracking alongside referral codes#19

Open
NeOMakinG wants to merge 6 commits intodevelopfrom
feat/swap-service-affiliate-address-tracking
Open

feat(swap-service): add affiliate address tracking alongside referral codes#19
NeOMakinG wants to merge 6 commits intodevelopfrom
feat/swap-service-affiliate-address-tracking

Conversation

@NeOMakinG
Copy link
Collaborator

@NeOMakinG NeOMakinG commented Feb 23, 2026

Summary

Adds B2B affiliate address tracking to swap-service with full on-chain verification for all 18 swappers, 46 chain adapters, and a comprehensive test suite. Runs alongside the existing referralCode system.

Epic: ss-aff — B2B Affiliate Tracking System (bead 4)

Changes

Prisma Schema (apps/swap-service/prisma/schema.prisma)

  • Added affiliateAddress String? field to Swap model
  • Added @@index([affiliateAddress]) for query performance

Migration

  • 20260223000000_add_affiliate_addressALTER TABLE "Swap" ADD COLUMN "affiliateAddress" TEXT + index

Swap Service (apps/swap-service/src/swaps/swaps.service.ts)

  • createSwap() now accepts and stores affiliateAddress
  • sellAmountUsd calculated and snapshot at creation time using getAssetPriceUsd()
  • New calculateAffiliateFees() method — queries by affiliateAddress
  • Enriched metadata for affiliate verification (+4 fields: affiliateBps, affiliateAddress, integratorFeeRecipient, sellAmountCryptoBaseUnit)
  • Commission rate fixed: referrer gets 10bps of swap volume (not 10% of the 60bps fee)
  • verifiedBps fallback to swap.affiliateBps when affiliateVerificationDetails.affiliateBps is undefined

Swap Controller (apps/swap-service/src/swaps/swaps.controller.ts)

  • New GET /swaps/affiliate-fees/:affiliateAddress endpoint
  • New DELETE /swaps/test-cleanup endpoint (marks old test-* swaps as FAILED for test automation)

Affiliate Verification (apps/swap-service/src/verification/swap-verification.service.ts)

  • All 18 swappers have verification handlers (1800+ lines)
  • On-chain verification for THORChain, MAYAChain, CowSwap, 0x, Portals, Bebop, Relay, ArbitrumBridge, ButterSwap
  • Metadata-based verification for Jupiter, AVNU, Sun.io, Cetus, STON.fi
  • API-based verification for NEAR Intents (1Click API), Relay (Relay API)

Chain Adapters (apps/swap-service/src/lib/chain-adapter-init.service.ts)

  • 46 chain adapters all initialized successfully at runtime
  • First-class EVM (9), Second-class EVM (24), UTXO (5), Cosmos SDK (3), Solana (1), Tron (1), Sui (1), Near (1), Starknet (1), Ton (1)

Environment Fixes

  • Starknet RPC: public.blastapi.iorpc.starknet.lava.build
  • THORChain node: daemon.thorchain.shapeshift.com (dead) → thornode.ninerealms.com
  • MAYAChain node: daemon.mayachain.shapeshift.com (dead) → mayanode.mayachain.info

Shared Types (packages/shared-types/src/index.ts)

  • Added affiliateAddress?: string to CreateSwapDto

Test Suite (tests/)

  • test-all-swappers.mjs — Comprehensive test script for all 18 swappers
  • TESTING.md — LLM-friendly guide for running and updating tests

Comprehensive Test Results

Test Methodology

The test script creates fake pending swaps using real confirmed on-chain tx hashes, then waits for the 5-second polling cron to resolve them. It validates:

  1. Swap creation via POST /swaps
  2. Status polling via checkTradeStatus (per-swapper implementation)
  3. Status resolution: PENDING → SUCCESS
  4. Affiliate verification and BPS detection
  5. Fee calculation with correct commission rate

Results: ✅ 14/17 SUCCESS, 3 expected PENDING, 0 failures

# Swapper Status Affiliate Verified BPS Check Method
1 THORChain ✅ SUCCESS No (not real affiliate tx) - Midgard API
2 MAYAChain ✅ SUCCESS No - Midgard API
3 CowSwap ✅ SUCCESS No - CoW Protocol API
4 0x ✅ SUCCESS No - checkEvmSwapStatus
5 Portals ✅ SUCCESS No - checkEvmSwapStatus
6 Bebop ✅ SUCCESS No - checkEvmSwapStatus
7 Jupiter ✅ SUCCESS Yes 60 checkSolanaSwapStatus
8 AVNU ✅ SUCCESS Yes 60 checkStarknetSwapStatus
9 Sun.io ✅ SUCCESS Yes 60 checkTronSwapStatus
10 Cetus ✅ SUCCESS Yes 60 checkSuiSwapStatus
11 Relay ✅ SUCCESS No (bps from Relay API) 85 Relay API
12 Arbitrum Bridge ✅ SUCCESS No - checkEvmSwapStatus (L2→L1)
13 ButterSwap ✅ SUCCESS No - checkEvmSwapStatus (same-chain)
14 NEAR Intents ✅ SUCCESS No (bps from real tx) 25 1Click API
15 STON.fi ⏳ PENDING (expected) - - TON adapter needs sender address
16 Across ⏳ PENDING (expected) - - Not a real Across deposit
17 Chainflip ⏳ PENDING (expected) - - Broker-specific swap ID needed

Affiliate Verification Types

Type Swappers How It Works
On-chain THORChain, MAYAChain, CowSwap, 0x, Portals, Bebop, Relay, ArbitrumBridge, ButterSwap Checks actual tx on-chain for affiliate fee data. Test txs aren't real affiliate swaps → correctly reports hasAffiliate=false
Metadata Jupiter, AVNU, Sun.io, Cetus, STON.fi Reads affiliateBps from enriched swap metadata. Reports hasAffiliate=true, bps=60
External API NEAR Intents, Relay Reads fee data from external API response

Fee Calculation

Field Value Notes
Affiliate BPS 60 Standard ShapeShift fee
Referrer Commission 10 bps of swap volume Fixed from previous 10% of fee
Origin handling web → 10bps, api → 10bps, null → 0 (open bug)
Affiliate Address 0xc770eefad204b5180df6a14ee197d99d808ee52d ShapeShift DAO treasury (used in tests)

API Endpoints for Checking Results

# Check affiliate fees for the test address
curl http://localhost:3001/swaps/affiliate-fees/0xc770eefad204b5180df6a14ee197d99d808ee52d

# Check referral fees by referral code
curl http://localhost:3001/swaps/referral-fees/<referralCode>

# List all pending swaps
curl http://localhost:3001/swaps/pending

# Get a specific swap
curl http://localhost:3001/swaps/<swapId>

Design Decisions

  • Two systems coexist: referralCode (user-to-user) and affiliateAddress (B2B) — both can be set on the same swap
  • Snapshot pricing: sellAmountUsd captured at creation time for consistent fee calculations
  • 10bps referrer commission: Calculated as 10bps of swap volume, not a percentage of the affiliate fee
  • Separate databases per service: Prisma db:push drops tables not in schema, so each service gets its own DB
  • No retry/age limits on polling: Swaps poll indefinitely until resolved (errors return PENDING, not FAILED)

Known Issues

Issue Impact Status
origin=null swaps get 0 commission Relay swap ($19.12) has origin=nullcommissionRate=0 Needs decision: treat null as web?
STON.fi needs sender address TON adapter parseTx() requires sender address not available in test Works with real swaps
Across needs real deposit tx Test uses random ETH tx, Across API doesn't recognize it Works with real bridge deposits
Chainflip needs broker-specific ID Swap IDs are scoped to the broker API key Works with real Chainflip swaps

Running the Tests

cd /path/to/shapeshift-backend

# Ensure all 3 services + PostgreSQL are running
yarn start:dev

# Run comprehensive test (all 17 swappers, ~2-3 min)
node tests/test-all-swappers.mjs

See tests/TESTING.md for the full guide including how to update tx hashes, troubleshooting, and environment setup.

Related PRs

  • Web: shapeshift/web — swap-tracking branch (public-api endpoints, widget fix, web app attribution, dashboard)

How to test:

  • Run the docker file env of the microservices
  • Update web to consume the services (uncomment the env urls for the microservices and comment the others)
  • Launch the widget as well
  • Launch the public-api
  • Try some swaps using the widget (evm, solana, btc...) make sure there is an affiliate address setup in the widget
  • Launch the referral dashboard from web and notice the referral fees shared are same (10bps if done through shapeshift referred or X BPS depending on the widget setup)
  • Try a swap the widget doesnt support, you should be redirected to shapeshift with a referral address and amounts/assets prefilled, execute the swap, notice the dashboard show the correct amounts

… codes

- Add affiliateAddress field to Swap model (nullable, indexed)
- Update CreateSwapDto to accept affiliateAddress
- Calculate and store sellAmountUsd at swap creation time
- Add calculateAffiliateFees method querying by affiliateAddress
- Add GET /swaps/affiliate-fees/:affiliateAddress endpoint
- Existing referralCode system unchanged
…n verification, and full lint cleanup

- Swap service: affiliate address tracking, fee asset resolution, configurable BPS
- Swap service: origin field (web/api/widget) with commission rate logic
- Swap service: live price calculation with distribution cutoff (5th of month)
- Verification service: per-protocol sell amount extraction (THORChain, Maya, CowSwap, Relay, Portals, Chainflip, 0x, Bebop, NEAR)
- Verification service: min(verified, db) anti-inflation for fee calculation
- Security: null origin returns 0% commission (safe default)
- Prisma schema: affiliateBps, origin, affiliateFeeAssetId, affiliateFeeAmountCryptoBaseUnit
- Lint: 277 errors → 0 across all services (proper TypeScript interfaces for external API responses, removed unnecessary async, typed Prisma queries)
@NeOMakinG NeOMakinG marked this pull request as ready for review February 25, 2026 19:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant