Referrals System
Overview
Wafra implements a sophisticated on-chain referral system that incentivizes user growth through automated fee sharing. The system operates entirely on-chain through smart contracts, providing transparent and trustless reward distribution based on proportional holdings and yield generation.
On-Chain Referral Architecture
Smart Contract Implementation
Core Contract Structure:
abstract contract FundReferrals is FundERC20 {
// Referral data structure with timestamp tracking
struct ReferralData {
address referrer;
uint256 createdAt;
}
// Mappings for referral relationships
mapping(address => ReferralData) private referralData;
using EnumerableMap for EnumerableMap.AddressToAddressMap;
EnumerableMap.AddressToAddressMap private referrals;
// Rate configuration
uint256 public referralRate; // 25% of fees (250000 / 1e6)
uint256 public referredRate; // 5% discount (50000 / 1e6)
}Referral Registration Process
On-Chain Registration:
function addReferral(address referrer) public {
require(referrer != msg.sender, "Cannot refer yourself");
require(referrer != address(0), "Invalid referrer address");
require(!referrals.contains(msg.sender), "Referral already exists");
uint256 timestamp = block.timestamp;
// Store in both mappings for efficiency
referrals.set(msg.sender, referrer);
referralData[msg.sender] = ReferralData(referrer, timestamp);
emit ReferralAdded(referrer, msg.sender, timestamp);
}Registration Requirements:
- Self-Referral Prevention: Users cannot refer themselves
- Unique Relationships: Each user can only have one referrer
- Immutable Links: Referral relationships cannot be changed once established
- Timestamp Tracking: Creation time recorded for potential time-based rules
Referral Data Management
Data Retrieval Functions:
function getReferralInfo(address referred)
external
view
returns (address referrer, uint256 createdAt, bool exists)
{
if (!referrals.contains(referred)) {
return (address(0), 0, false);
}
ReferralData memory data = referralData[referred];
return (data.referrer, data.createdAt, true);
}
function getReferrals() public view returns (Referral[] memory) {
address[] memory allReferrals = referrals.keys();
uint256 length = allReferrals.length;
Referral[] memory _referrals = new Referral[](length);
for (uint256 i = 0; i < length; i++) {
address referred = allReferrals[i];
_referrals[i] = Referral(
referred,
referrals.get(referred),
referralData[referred].createdAt
);
}
return _referrals;
}Fee Distribution Mechanism
Automated Distribution System
Distribution Process:
function distribute(uint256 fees) internal {
if (!_validateDistribution(fees)) return;
uint256 balance = balanceOf(address(this));
uint256 actualFees = fees > balance ? balance : fees;
// Process all referral relationships
uint256 distributedFees = _processReferralDistributions(actualFees);
// Send remaining fees to treasury
_sendRemainingToTreasury(actualFees, distributedFees);
emit Distribution(distributedFees);
}Proportional Reward Calculation
User Share Calculation:
function _distributeForReferralPair(
address referred,
address referrer,
uint256 createdAt,
uint256 balance,
uint256 supply,
uint256 fees
) private returns (uint256) {
// Calculate user's proportional share of total fees
uint256 userShare = (balance * fees) / supply;
if (userShare == 0) return 0;
uint256 totalDistributed = 0;
// Calculate and distribute referrer rewards (25% of user's share)
uint256 referralShare = (userShare * referralRate) / 1e6;
if (referralShare > 0) {
_transferScaled(address(this), referrer, referralShare);
emit ReferralRewardSent(referrer, referred, referralShare);
totalDistributed += referralShare;
}
// Calculate and distribute referred user discount (5% of user's share)
uint256 referredShare = (userShare * referredRate) / 1e6;
if (referredShare > 0) {
_transferScaled(address(this), referred, referredShare);
emit ReferredRewardSent(referred, referredShare);
totalDistributed += referredShare;
}
return totalDistributed;
}Rate Configuration System
Current Reward Structure:
// Rate configuration (parts per million)
uint256 public referralRate = 250000; // 25% of collected fees to referrer
uint256 public referredRate = 50000; // 5% discount to referred user
// Based on 2% APY management fee:
// Referrer receives: ~0.5% APY on referred user's balance
// Referred user receives: ~0.1% APY discount on their feesRate Management Functions:
function setReferralRate(uint256 _newRate) external onlyOwner {
require(_newRate <= 500000, "Rate cannot exceed 50%"); // 50% max
uint256 oldRate = referralRate;
referralRate = _newRate;
emit ReferralRateUpdated(oldRate, _newRate);
}
function setReferredRate(uint256 _newRate) external onlyOwner {
require(_newRate <= 100000, "Rate cannot exceed 10%"); // 10% max
uint256 oldRate = referredRate;
referredRate = _newRate;
emit ReferredRateUpdated(oldRate, _newRate);
}Off-Chain Referral Management
Database Integration
Referral Tracking Schema:
model Referral {
id Int @id @default(autoincrement())
referredPhone String // Phone number of referred user
userId String // ID of referrer
user User @relation(fields: [userId], references: [id])
accepted Boolean @default(false) // Whether on-chain registration occurred
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([userId, referredPhone])
@@index([userId])
@@index([referredPhone])
}Waitlist Referral System
Pre-Launch Referrals:
model Waitlist {
id Int @id @default(autoincrement())
email String @unique
createdAt DateTime @default(now())
referralCode String? // Optional referral code
@@index([referralCode])
}
model WaitlistReferral {
id Int @id @default(autoincrement())
name String
referralCode String @unique
createdAt DateTime @default(now())
@@index([referralCode])
}API Integration
tRPC Endpoints:
export const referralRouter = router({
// Get user's referral status
getReferralStatus: protectedProcedure.query(async ({ ctx }) => {
const user = ctx.session.user;
return await getReferralInfo(user.walletAddress);
}),
// Submit referral relationship
submitReferral: protectedProcedure
.input(
z.object({
referredPhone: z.string(),
})
)
.mutation(async ({ input, ctx }) => {
return await createReferralRelationship(ctx.session.user, input);
}),
// Accept referral (triggers on-chain registration)
acceptReferral: protectedProcedure
.input(
z.object({
referrerAddress: z.string(),
})
)
.mutation(async ({ input, ctx }) => {
return await acceptReferralOnChain(ctx.session.user, input);
}),
});Transaction Integration
Client-Side Referral Registration
Transaction Builder Implementation:
export class SetReferralTransactionBuilder extends TransactionBuilder<SetReferralParams> {
getTransactionType(): string {
return "setReferral";
}
validateParams(params: SetReferralParams): boolean {
return (
ethers.isAddress(params.referrerAddress) &&
params.referrerAddress !== ethers.ZeroAddress
);
}
async buildTransaction(
params: SetReferralParams,
context: ExecutionContext
): Promise<TransactionData> {
return {
to: context.fundAddress,
value: "0",
data: encodeFunctionData({
abi: fundAbi,
functionName: "addReferral",
args: [params.referrerAddress as `0x${string}`],
}),
operation: 0, // Call operation
};
}
}Mobile App Integration
Referral Hook:
export function useReferrals() {
const { user } = useAuth();
const trpc = api.useContext();
const submitReferral = useMutation({
mutationFn: async (data: ReferralSubmissionData) => {
return await trpc.referral.submitReferral.mutate(data);
},
onSuccess: () => {
// Trigger on-chain registration transaction
triggerReferralTransaction();
},
});
const acceptReferral = useMutation({
mutationFn: async (data: ReferralAcceptanceData) => {
const { prepareSetReferralTransaction } = await import(
"@wafra/transactions"
);
// Prepare and execute on-chain transaction
const result = await prepareSetReferralTransaction(
{ referrerAddress: data.referrerAddress },
await getTransactionContext()
);
return await executeTransaction(result);
},
onSuccess: () => {
// Update local state and refresh data
trpc.referral.getReferralStatus.invalidate();
},
});
return { submitReferral, acceptReferral };
}Reward Economics
Yield-Based Rewards
Current Reward Structure:
interface ReferralRewards {
// Management fee: 2% APY on total fund value
managementFee: {
annualRate: "2%";
weeklyRate: "0.0377%";
};
// Referrer rewards: 25% of referred user's fees
referrerReward: {
percentageOfFees: "25%";
effectiveAPY: "~0.5%"; // On referred user's balance
};
// Referred user discount: 5% reduction in fees
referredDiscount: {
percentageOfFees: "5%";
effectiveAPY: "~0.1%"; // Discount on their fees
};
// Treasury allocation: Remaining 70% of fees
treasuryAllocation: {
percentageOfFees: "70%";
purpose: "Protocol development and operations";
};
}Proportional Distribution Model
Balance-Weighted Rewards:
// Example calculation for $10,000 referred user balance
// Weekly management fee: $10,000 * 0.0377% = $3.77
// Referrer reward: $3.77 * 25% = $0.94 (weekly)
// Annual referrer reward: $0.94 * 52 = ~$49 (0.49% APY on referred balance)
// Referred user discount: $3.77 * 5% = $0.19 (weekly)
// Annual user discount: $0.19 * 52 = ~$10 (0.1% APY discount)
// Treasury allocation: $3.77 * 70% = $2.64 (weekly)Scalability Benefits
Network Effects:
- Exponential Growth: Each referred user can refer others
- Compound Rewards: Referrers benefit from their network’s growth
- Self-Sustaining: Reduced marketing costs through organic growth
- Quality Incentive: Rewards aligned with user retention and deposits
Security & Safety Features
Smart Contract Security
Protection Mechanisms:
// Prevent manipulation attempts
error SelfReferral();
error ZeroAddressReferrer();
error ReferralAlreadyExists();
error InsufficientFundTokenBalance(uint256 available, uint256 required);
// Validation functions
function _validateDistribution(uint256 fees) private view returns (bool) {
uint256 supply = totalSupply();
if (supply == 0) return false;
uint256 balance = balanceOf(address(this));
uint256 adjustedFees = (fees / PRECISION) * PRECISION;
if (balance < adjustedFees) {
revert InsufficientFundTokenBalance(balance, fees);
}
return true;
}Rate Limits & Constraints
Configurable Safety Limits:
// Maximum referral rates to prevent excessive rewards
uint256 public constant MAX_REFERRAL_RATE = 500000; // 50% maximum
uint256 public constant MAX_REFERRED_RATE = 100000; // 10% maximum
function setReferralRate(uint256 _newRate) external onlyOwner {
require(_newRate <= MAX_REFERRAL_RATE, "Rate exceeds maximum");
referralRate = _newRate;
}Audit Trail & Transparency
Event Logging:
event ReferralAdded(address indexed referrer, address indexed referred, uint256 timestamp);
event ReferralRewardSent(address indexed referrer, address indexed referred, uint256 amount);
event ReferredRewardSent(address indexed referred, uint256 amount);
event Distribution(uint256 distributedFees);
event ReferralRateUpdated(uint256 oldRate, uint256 newRate);Analytics & Monitoring
Performance Metrics
Key Performance Indicators:
interface ReferralAnalytics {
totalReferrals: number;
activeReferrers: number;
averageReferralsPerUser: number;
totalRewardsDistributed: bigint;
monthlyGrowthRate: number;
topReferrers: {
address: string;
referralCount: number;
totalRewards: bigint;
}[];
rewardDistribution: {
referrerRewards: bigint;
userDiscounts: bigint;
treasuryAllocation: bigint;
};
}Real-Time Tracking
Dashboard Metrics:
export class ReferralAnalyticsService {
async getReferralMetrics(): Promise<ReferralMetrics> {
// Get on-chain referral data
const onChainReferrals = await fundContract.read.getReferrals();
// Calculate reward distributions
const rewardData = await this.calculateRewardDistributions();
// Track growth metrics
const growthMetrics = await this.calculateGrowthMetrics();
return {
totalReferrals: onChainReferrals.length,
activeReferrers: this.countActiveReferrers(onChainReferrals),
rewardsSummary: rewardData,
growthTrends: growthMetrics,
};
}
private async calculateRewardDistributions(): Promise<RewardSummary> {
// Query Distribution events from smart contract
const events = await this.getDistributionEvents();
return events.reduce(
(acc, event) => {
acc.totalDistributed += event.amount;
acc.referrerRewards += event.referrerRewards;
acc.userDiscounts += event.userDiscounts;
return acc;
},
{
totalDistributed: 0n,
referrerRewards: 0n,
userDiscounts: 0n,
}
);
}
}Future Enhancements
Advanced Features
Planned Improvements:
- Tiered Rewards: Higher rates for high-volume referrers
- Time-Based Bonuses: Additional rewards for long-term referrals
- Group Referrals: Special programs for communities and organizations
- Cross-Chain Referrals: Support for multi-chain referral relationships
Governance Integration
Decentralized Management:
- Community Voting: Rate adjustments through governance proposals
- Dynamic Rates: Algorithm-based rate optimization
- Referral Contests: Community-driven growth campaigns
- Reward Pools: Special allocation for referral incentives
This referral system provides a robust foundation for organic growth while maintaining transparency, security, and fair reward distribution through automated smart contract execution.