Skip to content
125 changes: 124 additions & 1 deletion src/predifi.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod Predifi {
use core::poseidon::PoseidonTrait;
// oz imports
use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};

use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::security::{PausableComponent, ReentrancyGuardComponent};
use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};
Expand All @@ -18,6 +19,7 @@ pub mod Predifi {
use starknet::{
ClassHash, ContractAddress, get_block_timestamp, get_caller_address, get_contract_address,
};

use crate::base::errors::Errors;
use crate::base::events::Events::{
BetPlaced, DisputeRaised, DisputeResolved, EmergencyActionCancelled,
Expand Down Expand Up @@ -152,6 +154,11 @@ pub mod Predifi {
upgradeable: UpgradeableComponent::Storage,
#[substorage(v0)]
reentrancy_guard: ReentrancyGuardComponent::Storage,
//validator performance tracking
validator_reputation: Map<ContractAddress, u256>,
validator_success_count: Map<ContractAddress,u256>,
validator_fail_count:Map<ContractAddress,u256>,
validator_slashed:Map<ContractAddress,u256>,
}

/// @notice Events emitted by the Predifi contract.
Expand Down Expand Up @@ -215,8 +222,24 @@ pub mod Predifi {
UpgradeableEvent: UpgradeableComponent::Event,
#[flat]
ReentrancyGuardEvent: ReentrancyGuardComponent::Event,
ValidatorSlashed:ValidatorSlashed,
ValidatorPerformanceUpdated:ValidatorPerformanceUpdated,
}

#[derive(Drop,starknet::Event)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is an event file for these

pub struct ValidatorSlashed{
#[key]
pub validator:ContractAddress,
pub amount:u256,
pub reputation_after:u256,
}
#[derive(Drop,starknet::Event)]
pub struct ValidatorPerformanceUpdated{
#[key]
pub validator:ContractAddress,
pub success:bool,
pub reputation_after:u256,
}

#[derive(Drop, Hash)]
struct HashingProperties {
username: felt252,
Expand All @@ -228,7 +251,107 @@ pub mod Predifi {
id: felt252,
login: HashingProperties,
}
/// @notice Update validator performance and adjust reputation.
/// @param validator Address of the validator.
/// @param success True if validation was correct, false otherwise.
fn update_performance(ref self:ContractState,validator:ContractAddress,success:bool){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is better now

if(success){
let prev=self.validator_success_count.read(validator);
self.validator_success_count.write(validator,prev+1);

let rep=self.validator_reputation.read(validator)+1;
self.validator_reputation.write(validator,rep);

self.emit(ValidatorPerformanceUpdated{validator,success,reputation_after:rep});
} else{
let prev=self.validator_fail_count.read(validator);
self.validator_fail_count.write(validator,prev+1);

let rep=self.validator_reputation.read(validator);
let new_rep=if rep>0 {rep-1} else { 0 };
self.validator_reputation.write(validator,new_rep);

self.emit(ValidatorPerformanceUpdated{validator,success,reputation_after:new_rep});
}
}

/// @notice Slash a validator by reducing reputation and treasury.
/// @param validator Validator address to slash.
/// @param amount Amount to slash.
fn slash_validator(ref self:ContractState,validator:ContractAddress,amount:u256){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

slash shoulnt take in any amount, it should be halved ie /2

//Ensure only admin can call
self.accesscontrol.assert_only_role(DEFAULT_ADMIN_ROLE);

let prev_slashed=self.validator_slashed.read(validator);
self.validator_slashed.write(validator,prev_slashed+amount);

let rep=self.validator_reputation.read(validator);
let new_rep=if rep>amount {rep-amount} else {0};
self.validator_reputation.write(validator,new_rep);

//Deduct from validator protocol_treasury
let treasury=self.validator_treasuries.read(validator);
let updated_treasury=if treasury>amount {treasury-amount} else { 0 };
self.validator_treasuries.write(validator,updated_treasury);

self.emit(ValidatorSlashed{validator,amount,reputation_after:new_rep});
}

/// @notice Distribute validator fees among validators based on reputation.
/// @param pool_id ID fo the pool to distribute fees for.
fn distribute_validator_fees(ref self:ContractState,pool_id:u256){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this validator fee is to be distributed to only the validators of that pool that chose the correct option, not all validators
perform neccessary assert statements here, assert the pool has ended assert that the pool has been resolved and assert that the fees are shared to only the validators that chose the correct option, not all validators and not all validators of a pool

let total_fee=self.validator_fee.read(pool_id);
let validators_len: u64=self.validators.len().into();

//Compute total reputation
let mut total_rep: u256=0;
let mut i:u64=0;
loop{
if i==validators_len{
break;
}
let v = self.validators.at(i.into()).read();
total_rep+=self.validator_reputation.read(v);
i += 1;
}
let mut j: u64=0;
loop {
if j == validators_len {
break;
}
let v=self.validators.at(j.into()).read();
let rep=self.validator_reputation.read(v);
let share=if total_rep>0{
(rep*total_fee)/total_rep
} else{
total_fee/(self.validators.len().into())
};
let prev=self.validator_treasuries.read(v);
self.validator_treasuries.write(v,prev+share);
j+=1;
}
}

// -----------------------------------------
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment is not neccessary, pls remove

// Getter functions for frontend / tests
// -----------------------------------------

/// @notice Get validator reputation
fn get_validator_reputation(self:@ContractState, validator:ContractAddress)->u256{
self.validator_reputation.read(validator)
}
/// @notice Get validator success count
fn get_validator_success(self:@ContractState, validator:ContractAddress)->u256{
self.validator_success_count.read(validator)
}
/// @notice Get validator fail count
fn get_validator_slashed(self:@ContractState, validator:ContractAddress)->u256{
self.validator_slashed.read(validator)
}
/// @notice Get validator treasury
fn get_validator_treasury(self:@ContractState, validator:ContractAddress)->u256{
self.validator_treasuries.read(validator)
}
/// @notice Initializes the Predifi contract.
/// @param self The contract state.
/// @param token_addr The address of the STRK token contract.
Expand Down
80 changes: 80 additions & 0 deletions tests/test_predifi.cairo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wirte tests in the test validator use already existing setup and follow the standard there

we are using foundry to test not cfg tests

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all tests youre writting should be in the test validators, and youre required to use the existing test setup to make your tests modular

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also add edge case testing

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#[cfg(test)]
mod tests{
use super::super::Predifi;
use starknet::ContractAddress;
use starknet::testing::*;
fn addr(x:u126)->ContractAdress{
ContractAddress::from_u128(x)
}
#[test]
fn test_update_performance_success_and_fail(){
let(contract_address,mut state)=deploy_contract("src/predifi.cairo");

let v= addr(1);

//success path
Predifi::update_performance(&mut state, v, true);
let rep1=Predifi::get_validator_reputation(&state,v);
assert(rep1==1,'Reputation should increase');
assert(success_count==1,'Success count should increment');

//fail path
Predifi::update_performance(&mut state, v, false);
let rep2=Predifi::get_validator_reputation(&state,v);
let fail_count=Predifi::get_validator_fail(&state,v);
assert(rep2==1,'Reputation should decrease');
assert(fail_count==1,'Fail count should increment');
}
#[test]
fn test_slash_validator_reduces_rep_and_treasury(){
let (contract_address,mut state)=deploy_contract("src/predifi.cairo");
let v= addr(2);
//give validator some rep and treasury
state.validator_reputation.write(v,100);
state.validator_treasuries.write(v,100);

//slash 50
Predifi::slash_validator(&mut state,v,50);

let rep=Predifi::get_validator_reputation(&state,v);
let treasury=Predifi::get_validator_treasury(&state,v);
let slashed=Predifi::get_validator_slashed(&state,v);

assert(rep==50,'Reputation should replace by 50');
assert(treasury==50,'Treasury should replace by 50');
assert(slashed==50,'Slashed amount recorded');
#[test]
fn test_distribute_validator_fees_propotional(){
let(contract_address,mut state)=deploy_contract("src/predifi.cairo");
let v1=addr(10);
let v2=addr(20);
let v3=addr(30);

state.validators.push(v1);
state.validators.push(v2);
state.validators.push(v3);

state.validator_reputation.write(v1,10);
state.validator_reputation.write(v2,20);
state.validator_reputation.write(v3,30);

state.validator_fee.write(1,60);

Predifi::distribute_validator_fees(&mut state,1);

let t1=Predifi::get_validator_treasury(&state,v1);
let t2=Predifi::get_validator_treasury(&state,v2);
let t3=Predifi::get_validator_treasury(&state,v3);

assert(t1==10,'v1 should get 10');
assert(t2==10,'v2 should get 20');
assert(t3==10,'v3 should get 30');



}



}
}
Loading