-
Notifications
You must be signed in to change notification settings - Fork 15
Feature/revenue service fee #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 9 commits
2feeb02
f354df0
525336b
46111cc
e3f359f
ea0d838
069e99f
0df5476
83b2344
a5e180e
ac5e76f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,6 +41,23 @@ pub fn unpause(env: &Env) -> Result<(), VaultError> { | |
| Ok(()) | ||
| } | ||
|
|
||
| pub fn set_fee(env: &Env, new_fee_bps: u32) -> Result<(), VaultError> { | ||
| let admin = storage::get_admin(env).ok_or(VaultError::NotInitialized)?; | ||
| admin.require_auth(); | ||
| if new_fee_bps > 2000 { | ||
| return Err(VaultError::FeeTooHigh); | ||
| } | ||
| storage::set_fee_bps(env, new_fee_bps); | ||
| Ok(()) | ||
| } | ||
|
|
||
| pub fn set_treasury(env: &Env, treasury: &Address) -> Result<(), VaultError> { | ||
| let admin = storage::get_admin(env).ok_or(VaultError::NotInitialized)?; | ||
| admin.require_auth(); | ||
| storage::set_treasury(env, treasury); | ||
| Ok(()) | ||
| } | ||
|
|
||
| pub fn set_my_rate(env: &Env, expert: &Address, rate_per_second: i128) -> Result<(), VaultError> { | ||
| expert.require_auth(); | ||
|
|
||
|
|
@@ -155,32 +172,34 @@ pub fn finalize_session( | |
| return Err(VaultError::BookingNotPending); | ||
| } | ||
|
|
||
| // 4. Calculate payments. | ||
| // rate_per_second is stored in atomic units of the payment token, so this | ||
| // multiplication is safe for any token precision as long as the product fits i128. | ||
| let expert_pay = booking | ||
| .rate_per_second | ||
| .checked_mul(actual_duration as i128) | ||
| .ok_or(VaultError::Overflow)?; | ||
| let refund = booking.total_deposit - expert_pay; | ||
| // 4. Calculate payments | ||
| let gross_expert_pay = booking.rate_per_second * (actual_duration as i128); | ||
| let refund = booking.total_deposit - gross_expert_pay; | ||
|
|
||
| // Ensure calculations are valid | ||
| if expert_pay < 0 || refund < 0 { | ||
| if gross_expert_pay < 0 || refund < 0 { | ||
| return Err(VaultError::InvalidAmount); | ||
| } | ||
|
|
||
| let fee_bps = storage::get_fee_bps(env); | ||
| let fee_amount = (gross_expert_pay * fee_bps as i128) / 10_000; | ||
| let expert_net_pay = gross_expert_pay - fee_amount; | ||
|
|
||
| // 5. Get token contract | ||
| let token_address = storage::get_token(env); | ||
| let token_client = token::Client::new(env, &token_address); | ||
| let contract_address = env.current_contract_address(); | ||
|
|
||
| // 6. Execute transfers | ||
| // Pay expert | ||
| if expert_pay > 0 { | ||
| token_client.transfer(&contract_address, &booking.expert, &expert_pay); | ||
| if fee_amount > 0 { | ||
| if let Some(treasury) = storage::get_treasury(env) { | ||
| token_client.transfer(&contract_address, &treasury, &fee_amount); | ||
| } | ||
| } | ||
|
Comment on lines
+259
to
+263
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential issue: Fee amount may be silently retained in contract when treasury is not set. When Consider one of these approaches:
🔧 Option 1: Skip fee if no treasury (recommended) let fee_bps = storage::get_fee_bps(env);
- let fee_amount = (gross_expert_pay * fee_bps as i128) / 10_000;
- let expert_net_pay = gross_expert_pay - fee_amount;
+ let treasury_opt = storage::get_treasury(env);
+ let fee_amount = if treasury_opt.is_some() {
+ (gross_expert_pay * fee_bps as i128) / 10_000
+ } else {
+ 0
+ };
+ let expert_net_pay = gross_expert_pay - fee_amount;
// ...
if fee_amount > 0 {
- if let Some(treasury) = storage::get_treasury(env) {
+ if let Some(treasury) = treasury_opt {
token_client.transfer(&contract_address, &treasury, &fee_amount);
}
}🔧 Option 2: Require treasury when fee is setIn pub fn set_fee(env: &Env, new_fee_bps: u32) -> Result<(), VaultError> {
let admin = storage::get_admin(env).ok_or(VaultError::NotInitialized)?;
admin.require_auth();
if new_fee_bps > 2000 {
return Err(VaultError::FeeTooHigh);
}
+ if new_fee_bps > 0 && storage::get_treasury(env).is_none() {
+ return Err(VaultError::TreasuryNotSet); // New error variant needed
+ }
storage::set_fee_bps(env, new_fee_bps);
Ok(())
}🤖 Prompt for AI Agents |
||
|
|
||
| if expert_net_pay > 0 { | ||
| token_client.transfer(&contract_address, &booking.expert, &expert_net_pay); | ||
| } | ||
|
|
||
| // Refund user | ||
| if refund > 0 { | ||
| token_client.transfer(&contract_address, &booking.user, &refund); | ||
| } | ||
|
|
@@ -189,7 +208,7 @@ pub fn finalize_session( | |
| storage::update_booking_status(env, booking_id, BookingStatus::Complete); | ||
|
|
||
| // 8. Emit SessionFinalized event | ||
| events::session_finalized(env, booking_id, actual_duration, expert_pay); | ||
| events::session_finalized(env, booking_id, actual_duration, expert_net_pay, fee_amount); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -39,6 +39,15 @@ impl PaymentVaultContract { | |||||||||||||
| contract::unpause(&env) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| pub fn set_fee(env: Env, new_fee_bps: u32) -> Result<(), VaultError> { | ||||||||||||||
| contract::set_fee(&env, new_fee_bps) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| pub fn set_treasury(env: Env, treasury: Address) -> Result<(), VaultError> { | ||||||||||||||
| contract::set_treasury(&env, &treasury) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /// Set an expert's own rate per second | ||||||||||||||
| /// Transfer admin rights to a new address (Admin-only) | ||||||||||||||
| /// Old admin instantly loses all privileges | ||||||||||||||
|
||||||||||||||
| /// Set an expert's own rate per second | |
| /// Transfer admin rights to a new address (Admin-only) | |
| /// Old admin instantly loses all privileges | |
| /// Transfer admin rights to a new address (Admin-only) | |
| /// Old admin instantly loses all privileges | |
| pub fn transfer_admin(env: Env, new_admin: Address) -> Result<(), VaultError> { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@contracts/payment-vault-contract/src/lib.rs` around lines 50 - 52, The doc
comment "/// Set an expert's own rate per second" is placed above transfer_admin
but belongs to set_my_rate; move or remove the misplaced comment: remove that
line above the transfer_admin declaration (function transfer_admin) and ensure
the same comment is present directly above the set_my_rate function (function
set_my_rate) so documentation matches the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: LightForgeHub/SkillSphere-Contracts
Length of output: 1187
Use checked arithmetic for graceful error handling in
finalize_session.Lines 242–250 use raw
i128arithmetic. While the workspace enablesoverflow-checks = truein all Cargo profiles (which causes panics on overflow rather than silent wrapping), it's better to handle potential overflows explicitly and returnVaultError::Overflowinstead of crashing. This improves user experience and gives the contract control over error handling.Proposed fix
🤖 Prompt for AI Agents