Skip to content

Commit fb7cc60

Browse files
authored
Merge pull request #379 from Macnelson9/feat/fee-tier-system
feat: Fee Tier System — tier query functions (#218)
2 parents c9438b0 + 3a27ac0 commit fb7cc60

3 files changed

Lines changed: 148 additions & 2 deletions

File tree

contract/src/lib.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ pub use analytics::{
2424
};
2525
pub use errors::ContractError;
2626
pub use types::{
27+
ArbitrationConfig, ArbitratorReputation, ArbitratorVote, DisclosureGrant, DisputeResolution,
28+
InsurancePolicy, MultiSigConfig, Proposal, ProposalAction, ProposalStatus, Subscription,
29+
SubscriptionTier, TierConfig, TierStatus, TierThresholds, TemplateTerms, TemplateVersion,
30+
Trade, TradePrivacy, TradeStatus, TradeTemplate, UserTier, UserTierInfo, VotingSummary,
2731
ArbitrationConfig, CrossChainInfo, DisputeResolution, InsurancePolicy, KycStatus,
2832
OptionalMetadata, Trade, TradeStatus, UserCompliance, MAX_INSURANCE_PREMIUM_BPS,
2933
MAX_METADATA_SIZE,
@@ -1000,6 +1004,80 @@ impl StellarEscrowContract {
10001004
Ok(())
10011005
}
10021006

1007+
/// Query a user's current tier info.
1008+
pub fn get_user_tier(env: Env, user: Address) -> Option<UserTierInfo> {
1009+
storage::get_user_tier(&env, &user)
1010+
}
1011+
1012+
/// Query the current tier fee configuration.
1013+
pub fn get_tier_config(env: Env) -> Option<TierConfig> {
1014+
storage::get_tier_config(&env)
1015+
}
1016+
1017+
/// Query the effective fee bps for a user's next trade.
1018+
pub fn get_effective_fee_bps(env: Env, user: Address) -> Result<u32, ContractError> {
1019+
let base = get_fee_bps(&env)?;
1020+
Ok(tiers::effective_fee_bps(&env, &user, base))
1021+
}
1022+
1023+
/// Query the volume thresholds required to reach Silver and Gold tiers.
1024+
pub fn get_tier_thresholds(_env: Env) -> TierThresholds {
1025+
tiers::get_tier_thresholds()
1026+
}
1027+
1028+
/// Query additional trading volume a user needs to reach the next tier.
1029+
/// Returns 0 if the user is already at Gold or has a custom fee.
1030+
pub fn get_volume_to_next_tier(env: Env, user: Address) -> u64 {
1031+
let info = storage::get_user_tier(&env, &user).unwrap_or(UserTierInfo {
1032+
tier: UserTier::Bronze,
1033+
total_volume: 0,
1034+
custom_fee_bps: None,
1035+
});
1036+
tiers::volume_to_next_tier(&info)
1037+
}
1038+
1039+
/// Query a complete tier status snapshot for a user: current tier, cumulative
1040+
/// volume, volume needed to reach the next tier, and effective fee bps.
1041+
pub fn get_user_tier_status(env: Env, user: Address) -> Result<TierStatus, ContractError> {
1042+
require_initialized(&env)?;
1043+
tiers::get_tier_status(&env, &user)
1044+
}
1045+
1046+
// -------------------------------------------------------------------------
1047+
// Trade Templates
1048+
// -------------------------------------------------------------------------
1049+
1050+
/// Create a reusable trade template (owner = seller).
1051+
pub fn create_template(
1052+
env: Env,
1053+
owner: Address,
1054+
name: soroban_sdk::String,
1055+
terms: TemplateTerms,
1056+
) -> Result<u64, ContractError> {
1057+
require_initialized(&env)?;
1058+
owner.require_auth();
1059+
templates::create_template(&env, &owner, name, terms)
1060+
}
1061+
1062+
/// Update a template with new terms, bumping its version.
1063+
pub fn update_template(
1064+
env: Env,
1065+
caller: Address,
1066+
template_id: u64,
1067+
name: soroban_sdk::String,
1068+
terms: TemplateTerms,
1069+
) -> Result<(), ContractError> {
1070+
require_initialized(&env)?;
1071+
caller.require_auth();
1072+
templates::update_template(&env, &caller, template_id, name, terms)
1073+
}
1074+
1075+
/// Deactivate a template so it can no longer be used to create trades.
1076+
pub fn deactivate_template(
1077+
env: Env,
1078+
caller: Address,
1079+
template_id: u64,
1080+
) -> Result<(), ContractError> {
10031081
pub fn set_bridge_oracle(env: Env, oracle: Address) -> Result<(), ContractError> {
10041082
require_initialized(&env)?;
10051083
storage::get_admin(&env)?.require_auth();

contract/src/tiers.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ use soroban_sdk::{Address, Env};
22

33
use crate::errors::ContractError;
44
use crate::events;
5-
use crate::storage::{get_tier_config, get_user_tier, save_user_tier};
6-
use crate::types::{TierConfig, UserTier, UserTierInfo, TIER_GOLD_THRESHOLD, TIER_SILVER_THRESHOLD};
5+
use crate::storage::{get_fee_bps, get_tier_config, get_user_tier, save_user_tier};
6+
use crate::types::{
7+
TierConfig, TierStatus, TierThresholds, UserTier, UserTierInfo,
8+
TIER_GOLD_THRESHOLD, TIER_SILVER_THRESHOLD,
9+
};
710

811
pub fn effective_fee_bps(env: &Env, user: &Address, base_fee_bps: u32) -> u32 {
912
let info = match get_user_tier(env, user) {
@@ -112,3 +115,42 @@ pub fn remove_custom_fee(env: &Env, user: &Address) {
112115
info.tier = volume_tier(info.total_volume);
113116
save_user_tier(env, user, &info);
114117
}
118+
119+
/// Returns the configured volume thresholds for Silver and Gold tiers.
120+
pub fn get_tier_thresholds() -> TierThresholds {
121+
TierThresholds {
122+
silver_threshold: TIER_SILVER_THRESHOLD,
123+
gold_threshold: TIER_GOLD_THRESHOLD,
124+
}
125+
}
126+
127+
/// Returns how much additional trading volume is needed to reach the next tier.
128+
/// Returns 0 if the user is already at Gold tier or has a Custom fee.
129+
pub fn volume_to_next_tier(info: &UserTierInfo) -> u64 {
130+
match info.tier {
131+
UserTier::Bronze => TIER_SILVER_THRESHOLD.saturating_sub(info.total_volume),
132+
UserTier::Silver => TIER_GOLD_THRESHOLD.saturating_sub(info.total_volume),
133+
UserTier::Gold | UserTier::Custom => 0,
134+
}
135+
}
136+
137+
/// Returns a full tier status snapshot for a user, including their current tier,
138+
/// cumulative volume, volume needed for the next upgrade, and effective fee bps.
139+
pub fn get_tier_status(env: &Env, user: &Address) -> Result<TierStatus, ContractError> {
140+
let base_fee_bps = get_fee_bps(env)?;
141+
let info = get_user_tier(env, user).unwrap_or(UserTierInfo {
142+
tier: UserTier::Bronze,
143+
total_volume: 0,
144+
custom_fee_bps: None,
145+
});
146+
let to_next = volume_to_next_tier(&info);
147+
let fee_bps = effective_fee_bps(env, user, base_fee_bps);
148+
let has_custom = info.custom_fee_bps.is_some();
149+
Ok(TierStatus {
150+
current_tier: info.tier,
151+
total_volume: info.total_volume,
152+
volume_to_next_tier: to_next,
153+
effective_fee_bps: fee_bps,
154+
has_custom_fee: has_custom,
155+
})
156+
}

contract/src/types.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,32 @@ pub enum OptionalMetadata {
1010
Some(String),
1111
}
1212

13+
/// Volume thresholds required to reach each tier.
14+
#[contracttype]
15+
#[derive(Clone, Debug, Eq, PartialEq)]
16+
pub struct TierThresholds {
17+
/// Minimum cumulative trade volume (in token units) to reach Silver tier
18+
pub silver_threshold: u64,
19+
/// Minimum cumulative trade volume (in token units) to reach Gold tier
20+
pub gold_threshold: u64,
21+
}
22+
23+
/// A snapshot of a user's current tier standing, including progress toward the next tier.
24+
#[contracttype]
25+
#[derive(Clone, Debug, Eq, PartialEq)]
26+
pub struct TierStatus {
27+
/// The user's current tier
28+
pub current_tier: UserTier,
29+
/// Total cumulative trading volume recorded for this user
30+
pub total_volume: u64,
31+
/// Additional volume needed to reach the next tier (0 if already at Gold or Custom)
32+
pub volume_to_next_tier: u64,
33+
/// The effective fee in basis points for the user's next trade
34+
pub effective_fee_bps: u32,
35+
/// Whether the user has a custom fee rate assigned
36+
pub has_custom_fee: bool,
37+
}
38+
1339
#[contracttype]
1440
#[derive(Clone, Debug, Eq, PartialEq)]
1541
pub enum TradeStatus {

0 commit comments

Comments
 (0)