mirror of
https://github.com/pezkuwichain/pezkuwi-telegram-miniapp.git
synced 2026-07-22 10:55:43 +00:00
2017ae77da
Behavior-preserving lint cleanup of the 30 pre-existing warnings, fixed by category (no blanket suppression): - no-explicit-any (19): precise local types / ApiPromise for Substrate dynamic queries + 'as unknown as' casts (same pattern as TokensCard). - no-console (4): dev-gated (import.meta.env.DEV) / warn; main.tsx prod console-suppression kept with a scoped documented disable. - no-non-null-assertion (4): replaced '!' with explicit guards reusing the existing fallback/return paths. - react-hooks/exhaustive-deps (3): missing dep is 't' (i18n) — adding it would re-fire blockchain fetches on language change; kept deps with a documented intentional disable. Tighten the lint gate from --max-warnings 30 to 0 so no new warnings can land. Verified: tsc 0 errors, eslint --max-warnings 0 clean, vite build ok.
287 lines
8.2 KiB
TypeScript
287 lines
8.2 KiB
TypeScript
/**
|
|
* Referral System Integration with pallet_referral
|
|
* Based on pwap/shared/lib/referral.ts
|
|
*
|
|
* NOTE: pallet_referral is on People Chain (connected to KYC via OnKycApproved hook)
|
|
* Use peopleApi when calling these functions!
|
|
*
|
|
* Workflow:
|
|
* 1. User A calls initiateReferral(userB_address) -> creates pending referral
|
|
* 2. User B completes KYC and gets approved
|
|
* 3. Pallet automatically confirms referral via OnKycApproved hook
|
|
* 4. User A's referral count increases
|
|
*/
|
|
|
|
import type { ApiPromise } from '@pezkuwi/api';
|
|
import type { KeyringPair } from '@pezkuwi/keyring/types';
|
|
|
|
export interface ReferralInfo {
|
|
referrer: string;
|
|
createdAt: number;
|
|
}
|
|
|
|
export interface ReferralStats {
|
|
referralCount: number;
|
|
referralScore: number;
|
|
whoInvitedMe: string | null;
|
|
pendingReferral: string | null;
|
|
}
|
|
|
|
/**
|
|
* Check if the referral pallet is available on the chain
|
|
*/
|
|
function isReferralPalletAvailable(api: ApiPromise): boolean {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
return !!((api.query as any).referral && (api.query as any).referral.pendingReferrals);
|
|
}
|
|
|
|
/**
|
|
* Initiate a referral for a new user
|
|
*/
|
|
export function initiateReferral(
|
|
api: ApiPromise,
|
|
keypair: KeyringPair,
|
|
referredAddress: string
|
|
): Promise<string> {
|
|
return new Promise((resolve, reject) => {
|
|
const tx = api.tx.referral.initiateReferral(referredAddress);
|
|
|
|
tx.signAndSend(keypair, ({ status, dispatchError }) => {
|
|
if (dispatchError) {
|
|
if (dispatchError.isModule) {
|
|
const decoded = api.registry.findMetaError(dispatchError.asModule);
|
|
const error = `${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`;
|
|
reject(new Error(error));
|
|
} else {
|
|
reject(new Error(dispatchError.toString()));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (status.isInBlock || status.isFinalized) {
|
|
const hash = status.asInBlock?.toString() || status.asFinalized?.toString() || '';
|
|
resolve(hash);
|
|
}
|
|
}).catch(reject);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get the pending referral for a user
|
|
*/
|
|
export async function getPendingReferral(api: ApiPromise, address: string): Promise<string | null> {
|
|
try {
|
|
// Check if referral pallet exists
|
|
if (!isReferralPalletAvailable(api)) {
|
|
if (import.meta.env.DEV) {
|
|
console.warn('Referral pallet not available on this chain');
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const result = await (api.query.referral as any).pendingReferrals(address);
|
|
|
|
if (result.isEmpty) {
|
|
return null;
|
|
}
|
|
|
|
return result.toString();
|
|
} catch (error) {
|
|
console.error('Error fetching pending referral:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the number of successful referrals for a user
|
|
*/
|
|
export async function getReferralCount(api: ApiPromise, address: string): Promise<number> {
|
|
try {
|
|
// Check if referral pallet exists
|
|
if (!isReferralPalletAvailable(api)) {
|
|
return 0;
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const count = await (api.query.referral as any).referralCount(address);
|
|
return count.toNumber();
|
|
} catch (error) {
|
|
console.error('Error fetching referral count:', error);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get referral info for a user (who referred them)
|
|
*/
|
|
export async function getReferralInfo(
|
|
api: ApiPromise,
|
|
address: string
|
|
): Promise<ReferralInfo | null> {
|
|
try {
|
|
// Check if referral pallet exists
|
|
if (!isReferralPalletAvailable(api)) {
|
|
return null;
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const result = await (api.query.referral as any).referrals(address);
|
|
|
|
if (result.isEmpty) {
|
|
return null;
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const data = result.toJSON() as any;
|
|
return {
|
|
referrer: data.referrer,
|
|
createdAt: parseInt(data.createdAt),
|
|
};
|
|
} catch (error) {
|
|
console.error('Error fetching referral info:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate referral score based on referral count
|
|
*
|
|
* Score calculation:
|
|
* - 0 referrals = 0 points
|
|
* - 1-10 referrals = count * 10 points (10, 20, 30, ..., 100)
|
|
* - 11-50 referrals = 100 + (count - 10) * 5 points
|
|
* - 51-100 referrals = 300 + (count - 50) * 4 points
|
|
* - 101+ referrals = 500 points (maximum)
|
|
*/
|
|
export function calculateReferralScore(referralCount: number): number {
|
|
if (referralCount === 0) return 0;
|
|
if (referralCount <= 10) return referralCount * 10;
|
|
if (referralCount <= 50) return 100 + (referralCount - 10) * 5;
|
|
if (referralCount <= 100) return 300 + (referralCount - 50) * 4;
|
|
return 500; // Max score
|
|
}
|
|
|
|
/**
|
|
* Get comprehensive referral statistics for a user
|
|
*/
|
|
export async function getReferralStats(api: ApiPromise, address: string): Promise<ReferralStats> {
|
|
// Check if referral pallet exists first
|
|
if (!isReferralPalletAvailable(api)) {
|
|
return {
|
|
referralCount: 0,
|
|
referralScore: 0,
|
|
whoInvitedMe: null,
|
|
pendingReferral: null,
|
|
};
|
|
}
|
|
|
|
try {
|
|
const [referralCount, referralInfo, pendingReferral] = await Promise.all([
|
|
getReferralCount(api, address),
|
|
getReferralInfo(api, address),
|
|
getPendingReferral(api, address),
|
|
]);
|
|
|
|
const referralScore = calculateReferralScore(referralCount);
|
|
|
|
return {
|
|
referralCount,
|
|
referralScore,
|
|
whoInvitedMe: referralInfo?.referrer || null,
|
|
pendingReferral,
|
|
};
|
|
} catch (error) {
|
|
console.error('Error fetching referral stats:', error);
|
|
return {
|
|
referralCount: 0,
|
|
referralScore: 0,
|
|
whoInvitedMe: null,
|
|
pendingReferral: null,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get list of all users who were referred by this user
|
|
*/
|
|
export async function getMyReferrals(api: ApiPromise, referrerAddress: string): Promise<string[]> {
|
|
try {
|
|
// Check if referral pallet exists
|
|
if (!isReferralPalletAvailable(api)) {
|
|
return [];
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const entries = await (api.query.referral as any).referrals.entries();
|
|
|
|
const myReferrals = entries
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
.filter(([_key, value]: [any, any]) => {
|
|
if (value.isEmpty) return false;
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const data = value.toJSON() as any;
|
|
return data.referrer === referrerAddress;
|
|
})
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
.map(([key]: [any, any]) => {
|
|
const addressHex = key.args[0].toString();
|
|
return addressHex;
|
|
});
|
|
|
|
return myReferrals;
|
|
} catch (error) {
|
|
console.error('Error fetching my referrals:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Subscribe to referral events for real-time updates
|
|
*/
|
|
export async function subscribeToReferralEvents(
|
|
api: ApiPromise,
|
|
callback: (event: {
|
|
type: 'initiated' | 'confirmed';
|
|
referrer: string;
|
|
referred: string;
|
|
count?: number;
|
|
}) => void
|
|
): Promise<() => void> {
|
|
// Check if referral pallet exists - if not, return no-op unsubscribe
|
|
if (!isReferralPalletAvailable(api)) {
|
|
return () => {};
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const unsub = await api.query.system.events((events: any[]) => {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
events.forEach((record: any) => {
|
|
const { event } = record;
|
|
|
|
if (event.section === 'referral') {
|
|
if (event.method === 'ReferralInitiated') {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const [referrer, referred] = event.data as any;
|
|
callback({
|
|
type: 'initiated',
|
|
referrer: referrer.toString(),
|
|
referred: referred.toString(),
|
|
});
|
|
} else if (event.method === 'ReferralConfirmed') {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const [referrer, referred, newCount] = event.data as any;
|
|
callback({
|
|
type: 'confirmed',
|
|
referrer: referrer.toString(),
|
|
referred: referred.toString(),
|
|
count: newCount.toNumber(),
|
|
});
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
return unsub as unknown as () => void;
|
|
}
|