elrond_wasm::imports!();
elrond_wasm::derive_imports!();
pub const MAX_PERCENTAGE: u64 = 10_000u64;
pub const MAX_FEE_PERCENTAGE: u64 = 9_000u64;
pub const DEFAULT_REFERRAL_PERCENTAGE: u64 = 500u64;
pub type TierDetailsArg<M> = MultiValue3<ManagedBuffer<M>, BigUint<M>, u64>;
#[derive(TopEncode, TopDecode, NestedEncode, NestedDecode, TypeAbi, Clone, Debug, PartialEq)]
pub struct TierDetails<M: ManagedTypeApi> {
pub name: ManagedBuffer<M>,
pub min_volume: BigUint<M>,
pub fee_percent: u64,
}
#[elrond_wasm::module]
pub trait ReferralModule {
#[endpoint(registerReferralTag)]
fn register_referral_tag(&self, tag: ManagedBuffer) {
let caller = self.blockchain().get_caller();
require!(self.referral_tag_percent(&tag).is_empty(), "Tag already registered");
require!(self.user_tag_mapping(&caller).is_empty(), "User already owns a tag");
require!(!self.tier_details().is_empty(), "Tiers not set");
self.user_tag_mapping(&caller).set(tag.clone());
self.accumulated_volume(&tag).clear();
for tier in self.tier_details().iter() {
if tier.min_volume == BigUint::zero() {
self.referral_tag_percent(&tag).set(tier.fee_percent);
return;
}
}
sc_panic!("No tier with min volume 0");
}
#[endpoint(claimReferralFees)]
fn claim_referral_fees(&self) {
let caller = self.blockchain().get_caller();
require!(!self.user_tag_mapping(&caller).is_empty(), "Not a tag owner");
let user_tag = self.user_tag_mapping(&caller).get();
let amount = self.collected_tag_fees(&user_tag).get();
require!(amount > 0, "No fees to claim");
self.send().direct_esdt(&caller, &self.rewards_token().get(), 0, &amount);
self.collected_tag_fees(&user_tag).clear();
}
#[endpoint(updateTier)]
fn update_tier(&self) -> ManagedBuffer {
let caller = self.blockchain().get_caller();
require!(!self.user_tag_mapping(&caller).is_empty(), "Not a tag owner");
let user_tag = self.user_tag_mapping(&caller).get();
let user_volume = self.accumulated_volume(&user_tag).get();
let mut tier_name = ManagedBuffer::new();
let mut fee_percent = self.referral_tag_percent(&user_tag).get();
for tier in self.tier_details().iter() {
if user_volume >= tier.min_volume && tier.fee_percent > fee_percent {
fee_percent = tier.fee_percent;
tier_name = tier.name;
}
}
require!(fee_percent != self.referral_tag_percent(&user_tag).get(), "No tier upgrade found");
self.referral_tag_percent(&user_tag).set(fee_percent);
tier_name
}
#[endpoint(addTierDetails)]
fn add_tier_details(&self, tiers: MultiValueEncoded<TierDetailsArg<Self::Api>>) {
for tier in tiers.into_iter() {
let (name, min_volume, fee_percent) = tier.into_tuple();
require!(fee_percent < MAX_FEE_PERCENTAGE, "Invalid fee percentage");
let is_new = self.tier_details().insert(TierDetails {
name,
min_volume,
fee_percent
});
require!(is_new, "Tier already exists");
}
}
#[only_owner]
#[endpoint(removeTierDetails)]
fn remove_tier_details(&self, name: ManagedBuffer) {
let mut tier_details = self.tier_details();
for tier in tier_details.iter() {
if tier.name == name {
tier_details.swap_remove(&tier);
return;
}
}
sc_panic!("Tier not found");
}
#[only_owner]
#[endpoint(setReferralFeePercentage)]
fn set_referral_fee_percentage(&self, tag: ManagedBuffer, new_percentage: u64) {
require!(new_percentage < MAX_FEE_PERCENTAGE, "Invalid new percentage given");
require!(!self.referral_tag_percent(&tag).is_empty(), "Tag not found");
self.referral_tag_percent(&tag).set(new_percentage);
}
#[only_owner]
#[endpoint(removeReferralTag)]
fn remove_referral_tag(&self, user_address: ManagedAddress) {
let rewards_token = self.rewards_token().get();
let tag = self.user_tag_mapping(&user_address).get();
let collected_amount = self.collected_tag_fees(&tag).get();
if collected_amount > 0 {
self.send().direct_esdt(&user_address, &rewards_token, 0, &collected_amount);
}
self.accumulated_volume(&tag).clear();
self.referral_tag_percent(&tag).clear();
self.collected_tag_fees(&tag).clear();
self.user_tag_mapping(&user_address).clear();
}
#[view(getCollectedFeeAmount)]
fn get_collected_fee_amount(&self, address: ManagedAddress) -> BigUint {
let tag_mapping = self.user_tag_mapping(&address);
self.collected_tag_fees(&tag_mapping.get()).get()
}
fn subtract_referral_fee_and_update_collected_fees(&self, fee_amount: BigUint, tag: ManagedBuffer) -> BigUint {
let tag_percentage = self.referral_tag_percent(&tag).get();
if tag_percentage == 0 {
return fee_amount;
}
let referral_amount = &fee_amount * tag_percentage / MAX_PERCENTAGE;
self.collected_tag_fees(&tag).update(|x| *x += &referral_amount);
fee_amount - referral_amount
}
#[view(getUserTag)]
#[storage_mapper("user_tag_mapping")]
fn user_tag_mapping(&self, user: &ManagedAddress) -> SingleValueMapper<ManagedBuffer>;
#[storage_mapper("collected_tag_fees")]
fn collected_tag_fees(&self, tag: &ManagedBuffer) -> SingleValueMapper<BigUint>;
#[view(getReferralFeePercentage)]
#[storage_mapper("referral_tags_percent")]
fn referral_tag_percent(&self, tag: &ManagedBuffer) -> SingleValueMapper<u64>;
#[view(getTierDetails)]
#[storage_mapper("tier_details")]
fn tier_details(&self) -> UnorderedSetMapper<TierDetails<Self::Api>>;
#[view(getTagAccumulatedVolume)]
#[storage_mapper("accumulated_volume")]
fn accumulated_volume(&self, tag: &ManagedBuffer) -> SingleValueMapper<BigUint>;
#[view(getWrappedToken)]
#[storage_mapper("rewards_token")]
fn rewards_token(&self) -> SingleValueMapper<TokenIdentifier>;
}