gauthami vijay
gauthami vijay

Reputation: 61

Rust contract DispatchError

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

Answers (0)

Related Questions