Reputation: 61
I have a RUST contract to stake and unstake coins. I am able to stake multiple times but I am able to unstake only once as soon as the contract is deployed then I am getting contract is trapped error . This is my contract :
#![cfg_attr(not(feature = "std"), no_std, no_main)]
use ink::prelude::vec::Vec;
use ink::storage::traits::StorageLayout;
use ink::prelude::string::ToString;
use ink::prelude::string::String;
use ink::prelude::format;
use ink::storage::Mapping;
use parity_scale_codec::{Encode, Decode};
#[ink::contract]
mod staking_exchange {
use super::*;
// Define the DebugMessage event outside the impl block
#[ink(event)]
pub struct DebugMessage {
#[ink(topic)]
pub message: String,
}
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(StorageLayout, scale_info::TypeInfo))]
pub struct InterestRateChange {
pub rate: u64,
pub start_time: u64,
}
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(StorageLayout, scale_info::TypeInfo))]
pub struct StakeInfo {
pub amount: u64,
pub start_time: u64,
pub interest_accrued: u64,
}
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(StorageLayout, scale_info::TypeInfo))]
pub struct UnstakeRequest {
pub amount: u64,
pub request_time: u64,
pub processed: bool,
}
///temp remove after this
#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "std", derive(StorageLayout, scale_info::TypeInfo))]
pub struct TotalStakedInfo {
pub total_principal: u64,
pub total_interest: u64,
pub last_time: u64,
pub amount: u64,
pub history_length: u64,
}
#[ink(event)]
pub struct Unstaked {
#[ink(topic)]
pub user: AccountId,
pub principal: u64,
pub interest: u64,
pub timestamp: u64,
}
#[ink(storage)]
pub struct StakingExchange {
pub interest_rate_history: Vec<InterestRateChange>,
user_stakes: Mapping<AccountId, Vec<StakeInfo>>,
pub unstake_requests: Mapping<AccountId, Vec<UnstakeRequest>>,
pub cooling_period: u64,
pub fee_percentage: u64,
treasury: AccountId,
pub total_staked_in_contract: u64,
}
use ink::prelude::vec;
impl StakingExchange {
#[ink(constructor, payable)]
pub fn new(treasury: AccountId, cooling_period: u64, fee_percentage: u64) -> Self {
let interest_rate_change = InterestRateChange {
rate: 5, // 5% interest rate
start_time: Self::env().block_timestamp(),
};
Self {
interest_rate_history:vec![interest_rate_change],
user_stakes: Default::default(),
unstake_requests: Default::default(),
cooling_period,
fee_percentage,
treasury,
total_staked_in_contract: 0, // Initialize to zero
}
}
#[ink(message, payable)]
pub fn stake(&mut self) {
let caller = self.env().caller();
let amount: u64 = self.env().transferred_value().try_into().unwrap();
assert!(amount > 0, "Cannot stake 0");
let mut stakes = self.user_stakes.get(caller).unwrap_or_default();
stakes.push(StakeInfo {
amount,
start_time: self.env().block_timestamp(),
interest_accrued: 0,
});
self.user_stakes.insert(caller, &stakes);
// Update the total staked in the contract
self.total_staked_in_contract = self.total_staked_in_contract.checked_add(amount)
.expect("Overflow in total staked calculation");
self.env().emit_event(Staked {
user: caller,
amount,
timestamp: self.env().block_timestamp(),
});
}
#[ink(message)]
pub fn request_unstake(&mut self, amount: u64, request_id: u64) {
let caller = self.env().caller();
// Ensure the amount is greater than 0
assert!(amount > 0, "Cannot request unstake of 0");
// Ensure that the caller has enough staked to unstake the specified amount
let total_staked = self.get_total_staked(caller);
// Ensure that the requestId matches the length of unstakeRequests array
let unstake_requests = self.unstake_requests.get(caller).unwrap_or_default();
let total_pending_requests: u64 = unstake_requests.iter()
.filter(|request| !request.processed) // Filter for unprocessed requests
.map(|request| request.amount) // Map to the amount of each request
.sum(); // Sum the amounts
// Log the amounts for debugging
self.env().emit_event(DebugMessage {
message: format!("Total Staked: {}, Total Pending Requests: {}, Requested Amount: {}",
total_staked, total_pending_requests, amount),
});
let total_requested = amount.checked_add(total_pending_requests)
.expect("Overflow in total requested amount");
// Ensure that the user can unstake the requested amount considering pending requests
assert!(total_staked >= total_requested, "Insufficient available staked amount considering
pending requests");
assert!(request_id == unstake_requests.len() as u64, "Invalid request ID");
// Push the unstake request to the user's unstake request list
let mut new_unstake_requests = unstake_requests.clone();
new_unstake_requests.push(UnstakeRequest {
amount,
request_time: self.env().block_timestamp(),
processed: false,
});
self.unstake_requests.insert(caller, &new_unstake_requests);
// Emit an event
self.env().emit_event(UnstakeRequested {
user: caller,
amount,
request_id,
timestamp: self.env().block_timestamp(),
});
}
#[ink(message)]
pub fn complete_unstake(&mut self, request_index: u64) {
let caller = self.env().caller();
let mut unstake_requests = self.unstake_requests.get(caller).unwrap_or_default();
self.env().emit_event(DebugMessage {
message: format!("Unstake request of the caller: {:?}", unstake_requests),
});
// // Ensure the request index is valid
if (request_index as usize) >= unstake_requests.len() {
self.env().emit_event(DebugMessage {
message: "Invalid request index.".to_string(),
});
return; // Invalid request index
}
let request = &mut unstake_requests[request_index as usize];
// Ensure the unstake request is not processed
if request.processed {
self.env().emit_event(DebugMessage {
message: "Request already processed.".to_string(),
});
return; // Request already processed
}
self.env().emit_event(DebugMessage {
message: format!("Unstake request of the caller: {:?}", request),
});
// Ensure the cooling period has passed
let safe_timestamp = request.request_time.checked_add(self.cooling_period)
.expect("Overflow in block timestamp addition");
self.env().emit_event(DebugMessage {
message: format!("Unstake request of the caller: {:?}, {:?}, {}, {}",
safe_timestamp,request.request_time,self.env().block_timestamp(),self.cooling_period),
});
if self.env().block_timestamp() < safe_timestamp {
self.env().emit_event(DebugMessage {
message: "Cooling period not yet passed.".to_string(),
});
return; // Cooling period not yet passed
}
let amount:u64 = request.amount;
// Check the total staked amount for the user
let total_staked = self.get_total_staked(caller);
if total_staked < amount {
self.env().emit_event(DebugMessage {
message: "Insufficient staked amount.".to_string(),
});
return; // Insufficient staked amount
}
// Process the unstake (calculating principal and interest)
let (total_principal, total_interest) = self.process_unstake(caller, amount);
self.env().emit_event(DebugMessage {
message: format!("total_principal total_interest: {:?}, {}",
total_principal,total_interest),
});
// Calculate the fee (based on the interest) and final payout
let treasury_fee = total_interest
.checked_mul(self.fee_percentage)
.expect("Overflow in treasury fee multiplication")
.checked_div(100)
.expect("Division error in treasury fee");
self.env().emit_event(DebugMessage {
message: format!("treasury_fee {}", treasury_fee),
});
let interest_after_fee = total_interest
.checked_sub(treasury_fee)
.expect("Underflow in interest fee calculation");
self.env().emit_event(DebugMessage {
message: format!("interest_after_fee {:?}", interest_after_fee),
});
let total_amount = total_principal
.checked_add(interest_after_fee)
.expect("Overflow in total amount calculation");
self.env().emit_event(DebugMessage {
message: format!("total_amount {:?}", total_amount),
});
// Ensure the contract has enough balance to cover the unstake
let safe_total = total_amount.checked_add(treasury_fee).expect("Overflow in total balance
calculation");
if self.env().balance() < safe_total as u128 {
self.env().emit_event(DebugMessage {
message: "Contract does not have enough balance.".to_string(),
});
return; // Contract does not have enough balance
}
// Transfer the treasury fee to the treasury account
self.env().transfer(self.treasury, treasury_fee as u128)
.expect("Treasury transfer failed");
// Transfer the remaining amount (principal + interest after fee) to the user
self.env().transfer(caller, total_amount as u128)
.expect("Transfer to user failed");
// Emit the Unstaked event
self.env().emit_event(Unstaked {
user: caller,
principal: total_principal,
interest: total_interest,
timestamp: self.env().block_timestamp(),
});
// Mark the request as processed
request.processed = true;
// Update the unstake_requests mapping
self.unstake_requests.insert(caller, &unstake_requests);
}
fn process_unstake(&mut self, user: AccountId, amount: u64) -> (u64, u64) {
let mut remaining_amount : u64 = amount;
let mut total_principal: u64 = 0;
let mut total_interest: u64 = 0;
let mut stakes = self.user_stakes.get(user).unwrap_or_default();
self.env().emit_event(DebugMessage {
message: format!("staked Amount, remianing_amount: {:?}", stakes),
});
for stake_info in stakes.iter_mut() {
let stake_amount = stake_info.amount;
self.env().emit_event(DebugMessage {
message: format!("staked Amount, remianing_amount: {:?},{}",
stake_amount,remaining_amount),
});
if stake_amount > 0 {
// Unpack the returned values from calculate_interest
let accrued_interest = self.calculate_interest(stake_info);
self.env().emit_event(DebugMessage {
message: format!("Accured_interest {}", accrued_interest),
});
if stake_amount >= remaining_amount {
self.env().emit_event(DebugMessage {
message: format!("stake is grater"),
});
let interest_for_unstaked = accrued_interest.checked_mul(remaining_amount)
.expect("Overflow detected")
.checked_div(stake_amount)
.expect("Division error");
self.env().emit_event(DebugMessage {
message: format!("interest_for_unstaked {}",
interest_for_unstaked),
});
total_interest = total_interest.checked_add(interest_for_unstaked)
.expect("Overflow detected");
total_principal = total_principal.checked_add(remaining_amount)
.expect("Overflow detected");
stake_info.amount = stake_info.amount.checked_sub(remaining_amount)
.expect("Underflow detected");
self.env().emit_event(DebugMessage {
message: format!("principal {}, interest {}", total_principal ,
total_interest),
});
if stake_info.amount == 0 {
stake_info.interest_accrued = 0;
} else {
stake_info.interest_accrued =
stake_info.interest_accrued.checked_add(accrued_interest)
.expect("Overflow detected")
.checked_sub(interest_for_unstaked)
.expect("Underflow detected");
stake_info.start_time = self.env().block_timestamp();
}
remaining_amount = 0;
break;
} else {
total_interest = total_interest.checked_add(accrued_interest)
.expect("Overflow detected");
total_principal = total_principal.checked_add(stake_amount)
.expect("Overflow detected");
remaining_amount = remaining_amount.checked_sub(stake_amount)
.expect("Underflow detected");
stake_info.amount = 0;
stake_info.interest_accrued = 0;
}
}
}
self.user_stakes.insert(user, &stakes);
(total_principal, total_interest)
}
fn calculate_interest(&self, stake_info: &StakeInfo) -> u64 {
let mut total_interest: u64 = 0;
let mut last_time = stake_info.start_time;
let amount = stake_info.amount;
let history_length = self.interest_rate_history.len() as u64;
self.env().emit_event(DebugMessage {
message: format!("Starting interest calculation. Amount: {}, Start time: {}", amount,
last_time),
});
let mut rate_amount: u64 = 0;
let mut time_period_it: u64 = 0;
let mut interest: u64 = 0;
// Iterate over the history of interest rates
for i in 1..history_length {
let rate_change = &self.interest_rate_history[i as usize];
if rate_change.start_time > last_time {
let time_period = rate_change.start_time.checked_sub(last_time)
.expect("Underflow in time period calculation");
let previous_index = i.checked_sub(1).expect("Index underflow"); // Precompute the
safe index
let previous_rate = self.interest_rate_history[previous_index as usize].rate; //
Use the precomputed index
self.env().emit_event(DebugMessage {
message: format!("Rate change detected. Time period: {}, Rate: {}",
time_period, previous_rate),
});
let interest_for_period = amount
.checked_mul(previous_rate)
.expect("Overflow in multiplication of amount and rate")
.checked_mul(time_period)
.expect("Overflow in multiplication of result and time period")
.checked_div(1000)
.expect("Time converstion error ")
.checked_div(365 * 24 * 60 * 60 * 100)
.expect("Division error in calculating interest for period");
self.env().emit_event(DebugMessage {
message: format!("Interest for period: {}", interest_for_period),
});
total_interest = total_interest
.checked_add(interest_for_period)
.expect("Overflow in total interest calculation");
last_time = rate_change.start_time;
}
}
// // Handle remaining time if still in the current rate period
if last_time <= self.env().block_timestamp() {
let block_time = self.env().block_timestamp();
let time_period = block_time.checked_sub(last_time)
.expect("Underflow in block timestamp subtraction");
let index = history_length.checked_sub(1).expect("History length underflow") as usize;
let rate = self.interest_rate_history.get(index)
.expect("Index out of bounds in interest rate history")
.rate;
self.env().emit_event(DebugMessage {
message: format!("Final rate for remaining time. Rate: {}, Time period: {}", rate,
time_period),
});
let interest_for_period = amount
.checked_mul(rate)
.expect("Overflow in multiplication")
.checked_mul(time_period)
.expect("Overflow in time period multiplication")
.checked_div(1000)
.expect("Seconds conversion")
.checked_div(365 * 24 * 60 * 60 * 100)
.expect("Division error");
self.env().emit_event(DebugMessage {
message: format!("Final interest for period: {}", interest_for_period),
});
rate_amount = rate;
time_period_it = time_period as u64;
total_interest = total_interest
.checked_add(interest_for_period)
.expect("Overflow in interest calculation");
}
self.env().emit_event(DebugMessage {
message: format!("Total interest calculated: {}", total_interest),
});
total_interest
}
#[ink(message)]
pub fn get_fee_percentage(&self) -> u64 {
self.fee_percentage
}
#[ink(message)]
pub fn get_cooling_period(&self) -> u64 {
self.cooling_period
}
#[ink(message)]
pub fn get_current_interest_rate(&self) -> u64 {
// Return the most recent interest rate
self.interest_rate_history
.last()
.map_or(0, |rate_change| rate_change.rate) // Returns 0 if the history is empty
}
#[ink(message)]
pub fn get_total_staked_in_contract(&self) -> u64 {
self.total_staked_in_contract
}
#[ink(message)]
pub fn set_fee_percentage(&mut self, new_fee_percentage: u64) {
// Ensure that only the contract owner (treasury) can update the fee percentage
assert_eq!(self.env().caller(), self.treasury, "Only the treasury can update the fee
percentage");
// Update the fee percentage
self.fee_percentage = new_fee_percentage;
}
#[ink(message)]
pub fn set_cooling_period(&mut self, new_cooling_period: u64) {
// Ensure that only the contract owner (treasury) can update the cooling period
assert_eq!(self.env().caller(), self.treasury, "Only the treasury can update the
cooling period");
// Update the cooling period
self.cooling_period = new_cooling_period;
}
#[ink(message)]
pub fn set_interest_rate(&mut self, new_rate: u64) {
// Ensure that only the contract owner (treasury) can update the interest rate
assert_eq!(self.env().caller(), self.treasury, "Only the treasury can update the
interest rate");
// Add a new entry to the interest rate history
let new_rate_change = InterestRateChange {
rate: new_rate,
start_time: self.env().block_timestamp(),
};
self.interest_rate_history.push(new_rate_change);
}
#[ink(message)]
pub fn get_total_staked_with_interest_for_caller(&self) -> (u64, u64) {
// Get the caller's address (AccountId)
let caller = self.env().caller();
// Fetch all the stakes for the caller
let stakes = self.user_stakes.get(caller).unwrap_or_default();
// Check and log the number of stakes for the caller
self.env().emit_event(DebugMessage {
message: format!("Number of stakes for caller: {} ", stakes.len()),
});
// // Initialize variables for total principal and total interest
let mut total_principal: u64 = 0;
let mut total_interest: u64 = 0;
let mut last_time: u64 = 0;
let mut amount: u64 = 0;
let mut rate: u64 = 0;
for stake_info in stakes.iter() {
self.env().emit_event(DebugMessage {
message: format!("Stake amount: {}", stake_info.amount),
});
total_principal = total_principal.checked_add(stake_info.amount)
.expect("Overflow in adding total staked");
self.env().emit_event(DebugMessage {
message: format!("Stake Info - Amount: {}, Start Time: {}, Interest Accrued: {}",
stake_info.amount, stake_info.start_time, stake_info.interest_accrued),
});
let interest_temp = self.calculate_interest(stake_info);
let accrued_interest = interest_temp;
self.env().emit_event(DebugMessage {
message: format!("Accrued interest for stake: {}", accrued_interest),
});
total_interest = total_interest.checked_add(accrued_interest)
.expect("Overflow in adding accrued interest");
}
(total_principal, total_interest)
}
#[ink(message)]
pub fn get_total_staked(&self, user: AccountId) -> u64 {
let stakes = self.user_stakes.get(user).unwrap_or_default();
stakes.iter().map(|stake| stake.amount).sum()
}
}
#[ink(event)]
pub struct Staked {
#[ink(topic)]
pub user: AccountId,
pub amount: u64,
pub timestamp: u64,
}
#[ink(event)]
pub struct UnstakeRequested {
#[ink(topic)]
pub user: AccountId,
pub amount: u64,
pub request_id: u64,
pub timestamp: u64,
}
}
I get this error when I try to access the complete_unstake and the get_total_stked_with_interest [ { "type": "sp_runtime:DispatchError", "type_name": "DispatchError", "value": { "Module": { "error": "0x0c000000", "index": 18 } }, "name": "dispatch_error" }, { "type": "frame_support:dispatch:DispatchInfo", "type_name": "DispatchInfo", "value": { "class": "Normal", "pays_fee": "Yes", "weight": { "proof_size": 65647, "ref_time": 4347126566 } }, "name": "dispatch_info" } ]`
link for the transaction : https://alephzero-testnet.subscan.io/block/75612794?tab=event&event=75612794-6
Why is this error happening . Is this because of the Integer overflows ? need guidance here please
Upvotes: 0
Views: 22