Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/base/types.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub struct Job {
pub updated_at: u64,
pub created_at: u64,
}

#[derive(Drop, Serde, starknet::Store, Clone)]
pub struct Applicant {
pub address: ContractAddress,
Expand Down Expand Up @@ -63,6 +63,14 @@ pub enum DisputeStatus {
Closed,
}

#[derive(Debug, Drop, Serde, starknet::Store, Clone, PartialEq)]
enum UserVerificationStatus {
Unverified,
Pending,
Verified,
Rejected,
}

// Added Copy derive so `Dispute` instances can be duplicated without moving,
// which fixes "Variable was previously moved" errors in contract logic that
// needs to access the struct multiple times.
Expand Down Expand Up @@ -100,3 +108,21 @@ pub struct ArbitratorInfo {
pub address: ContractAddress,
pub reputation: u256,
}


#[derive(Drop, Serde, starknet::Store, Clone)]
pub struct Profile {
pub profile_address: ContractAddress,
pub name: felt252,
pub bio: felt252,
}


#[derive(Drop, Serde, starknet::Store, Clone)]
pub struct WorkEntries {
pub org: felt252,
pub role: felt252,
pub duration_months: u64,
pub description: felt252,
}

48 changes: 35 additions & 13 deletions src/contracts/Dispute.cairo
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
#[starknet::contract]
pub mod Dispute {
use starkhive_contract::base::types::{Dispute, DisputeStatus, Evidence, VoteInfo, ArbitratorInfo};
use starkhive_contract::base::types::{
ArbitratorInfo, Dispute, DisputeStatus, Evidence, VoteInfo,
};
use starkhive_contract::interfaces::IDispute::IDispute;
use starknet::storage::{Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess, StoragePointerWriteAccess};
use starknet::{ContractAddress, contract_address_const, get_block_timestamp, get_caller_address};
use starknet::storage::{
Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess,
StoragePointerWriteAccess,
};
use starknet::{
ContractAddress, contract_address_const, get_block_timestamp, get_caller_address,
};

// --------------- Storage -----------------
#[storage]
struct Storage {
disputes: Map<u256, Dispute>, // dispute_id -> Dispute
disputes: Map<u256, Dispute>, // dispute_id -> Dispute
dispute_counter: u256,
evidence_counter: Map<u256, u256>, // dispute_id -> next evidence id
evidences: Map<(u256, u256), Evidence>, // (dispute_id, evidence_id) -> Evidence
evidence_counter: Map<u256, u256>, // dispute_id -> next evidence id
evidences: Map<(u256, u256), Evidence>, // (dispute_id, evidence_id) -> Evidence
votes: Map<(u256, ContractAddress), VoteInfo>, // (dispute_id, arbitrator) -> VoteInfo
arbitrators: Map<ContractAddress, ArbitratorInfo>, // reputation mapping
multi_sig: ContractAddress, // multi-sig wallet that can penalise
multi_sig: ContractAddress // multi-sig wallet that can penalise
}

// --------------- Events ------------------
Expand Down Expand Up @@ -127,11 +134,16 @@ pub mod Dispute {
fn submit_evidence(ref self: ContractState, dispute_id: u256, data: ByteArray) {
let caller = get_caller_address();
let dispute = self.disputes.read(dispute_id);
assert(dispute.status == DisputeStatus::Open || dispute.status == DisputeStatus::Voting, 'wrong_status');
assert(
dispute.status == DisputeStatus::Open || dispute.status == DisputeStatus::Voting,
'wrong_status',
);

let next_id = self.evidence_counter.read(dispute_id) + 1;
let now = get_block_timestamp();
let ev = Evidence { dispute_id, evidence_id: next_id, submitter: caller, data, submitted_at: now };
let ev = Evidence {
dispute_id, evidence_id: next_id, submitter: caller, data, submitted_at: now,
};
self.evidences.write((dispute_id, next_id), ev);
self.evidence_counter.write(dispute_id, next_id);
self.emit(EvidenceSubmitted { dispute_id, evidence_id: next_id, submitter: caller });
Expand All @@ -154,7 +166,9 @@ pub mod Dispute {
// reputation weight
let arbitrator_info = self.arbitrators.read(caller);
let weight: u256 = 1_u256;
let vi = VoteInfo { dispute_id, arbitrator: caller, support, weight, submitted_at: now };
let vi = VoteInfo {
dispute_id, arbitrator: caller, support, weight, submitted_at: now,
};
self.votes.write((dispute_id, caller), vi);

// Work around Cairo move semantics: move `dispute` once into a new mutable var,
Expand All @@ -172,7 +186,10 @@ pub mod Dispute {
let mut dispute = self.disputes.read(dispute_id);
let now = get_block_timestamp();
assert(now >= dispute.voting_deadline, 'too_early');
assert(dispute.status == DisputeStatus::Voting || dispute.status == DisputeStatus::Open, 'wrong_status');
assert(
dispute.status == DisputeStatus::Voting || dispute.status == DisputeStatus::Open,
'wrong_status',
);

// Simplified: first implementation just awards claimant.
let winner = dispute.clone().claimant;
Expand All @@ -185,7 +202,10 @@ pub mod Dispute {
let caller = get_caller_address();
let mut dispute = self.disputes.read(dispute_id);
assert(dispute.status == DisputeStatus::Resolved, 'not_resolved');
assert(caller == dispute.clone().claimant || caller == dispute.clone().respondent, 'only_party');
assert(
caller == dispute.clone().claimant || caller == dispute.clone().respondent,
'only_party',
);
dispute.status = DisputeStatus::Appealed;
dispute.voting_deadline = get_block_timestamp() + 2 * 24 * 60 * 60; // 2 days
self.disputes.write(dispute_id, dispute);
Expand All @@ -207,7 +227,9 @@ pub mod Dispute {
d
}

fn get_vote(self: @ContractState, dispute_id: u256, arbitrator: ContractAddress) -> VoteInfo {
fn get_vote(
self: @ContractState, dispute_id: u256, arbitrator: ContractAddress,
) -> VoteInfo {
let v = self.votes.read((dispute_id, arbitrator));
v
}
Expand Down
100 changes: 100 additions & 0 deletions src/contracts/UserProfileRegistry.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use starkhive_contract::base::types::{Profile, WorkEntries};
use starknet::storage::{
Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess,
StoragePointerWriteAccess,
};
use starknet::{ContractAddress, contract_address_const, get_block_timestamp, get_caller_address};

// --------------- Storage -----------------
#[storage]
struct Storage {
profiles: Map<ContractAddress, Profile>,
portfolio_links: Map<(ContractAddress, u256), felt252>,
portfolio_count: Map<ContractAddress, u256>,
skills: Map<(ContractAddress, u256), felt252>,
skill_count: Map<ContractAddress, u256>,
skill_verified: Map<(ContractAddress, felt252), bool>,
work_entries: Map<(ContractAddress, u256), WorkEntries>,
work_entry_count: Map<ContractAddress, u256>,
profile_is_public: Map<ContractAddress, bool>,
reputation_score: Map<ContractAddress, u256>,
}


// --------------- Events ------------------
#[derive(Drop, starknet::Event)]
pub struct ProfileCreated {
#[key]
pub profile_address: ContractAddress,
pub name: felt252,
pub bio: felt252,
}

#[derive(Drop, starknet::Event)]
pub struct ProfileUpdated {
#[key]
pub profile_address: ContractAddress,
pub name: felt252,
pub bio: felt252,
}

#[derive(Drop, starknet::Event)]
pub struct PortfolioLinkAdded {
#[key]
pub profile_address: ContractAddress,
pub index: u256,
pub link: felt252,
}

#[derive(Drop, starknet::Event)]
pub struct SkillAdded {
#[key]
pub profile_address: ContractAddress,
pub index: u256,
pub skill: felt252,
}

#[derive(Drop, starknet::Event)]
pub struct SkillVerified {
#[key]
pub profile_address: ContractAddress,
pub skill: felt252,
}

#[derive(Drop, starknet::Event)]
pub struct WorkEntryAdded {
#[key]
pub profile_address: ContractAddress,
pub index: u256,
pub org: felt252,
pub role: felt252,
pub duration_months: u64,
pub description: felt252,
}

#[derive(Drop, starknet::Event)]
pub struct ProfilePrivacyToggled {
#[key]
pub profile_address: ContractAddress,
pub is_public: bool,
}

#[derive(Drop, starknet::Event)]
pub struct ReputationUpdated {
#[key]
pub profile_address: ContractAddress,
pub new_score: u256,
}


// --------------- Impl --------------------
#[abi(embed_v0)]
impl ProfileRegistryImpl of IDispute<ContractState> {
// Multi-sig init (called once)
fn init(ref self: ContractState, multi_sig: ContractAddress) {
let zero: ContractAddress = contract_address_const::<'0x0'>();
let current: ContractAddress = self.multi_sig.read();
assert(current == zero, 'only_multisig');
self.multi_sig.write(multi_sig);
}
}
14 changes: 3 additions & 11 deletions src/interfaces/IDispute.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use starkhive_contract::base::types::{Dispute, VoteInfo, ArbitratorInfo};
use starkhive_contract::base::types::{ArbitratorInfo, Dispute, VoteInfo};
use starknet::ContractAddress;

#[starknet::interface]
Expand All @@ -12,17 +12,9 @@ pub trait IDispute<TContractState> {
respondent: ContractAddress,
) -> u256;

fn submit_evidence(
ref self: TContractState,
dispute_id: u256,
data: ByteArray,
);
fn submit_evidence(ref self: TContractState, dispute_id: u256, data: ByteArray);

fn vote(
ref self: TContractState,
dispute_id: u256,
support: bool,
);
fn vote(ref self: TContractState, dispute_id: u256, support: bool);

fn resolve_dispute(ref self: TContractState, dispute_id: u256);

Expand Down
39 changes: 39 additions & 0 deletions src/interfaces/IProfileRegistry.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use starkhive_contract::base::types::{ArbitratorInfo, Dispute, VoteInfo};
use starknet::ContractAddress;

#[starknet::interface]
pub trait IProfileRegistry<TContractState> {
// === Profile Actions ===
fn register_profile(ref self: TContractState, username: felt252, bio: felt252, avatar: felt252);

fn update_profile(ref self: TContractState, field: felt252, value: felt252);

fn toggle_profile_visibility(ref self: TContractState);

// === Portfolio Links ===
fn add_portfolio_link(ref self: TContractState, link: felt252);

fn get_portfolio_link(self: @TContractState, user: ContractAddress, index: u256) -> felt252;
fn get_portfolio_count(self: @TContractState, user: ContractAddress) -> u256;

// === Skills ===
fn add_skill(ref self: TContractState, skill: felt252);
fn verify_skill(ref self: TContractState, user: ContractAddress, skill: felt252);

fn get_skill(self: @TContractState, user: ContractAddress, index: u256) -> felt252;
fn get_skill_count(self: @TContractState, user: ContractAddress) -> u256;
fn is_skill_verified(self: @TContractState, user: ContractAddress, skill: felt252) -> bool;

// === Work Experience ===
fn add_work_entry(
ref self: TContractState, org: felt252, role: felt252, months: u32, description: felt252,
);

fn get_work_entry(self: @TContractState, user: ContractAddress, index: u256) -> WorkEntry;
fn get_work_entry_count(self: @TContractState, user: ContractAddress) -> u256;

// === Profile + Reputation Views ===
fn get_profile(self: @TContractState, user: ContractAddress) -> Profile;
fn get_reputation_score(self: @TContractState, user: ContractAddress) -> u256;
fn is_profile_public(self: @TContractState, user: ContractAddress) -> bool;
}
6 changes: 3 additions & 3 deletions src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
pub mod contracts {
pub mod Dispute;
pub mod Jobs;
pub mod MockUSDC;
pub mod Dispute;
}
pub mod base {
pub mod types;
}
pub mod interfaces {
pub mod IJobs;
pub mod IDispute;
}
pub mod IJobs;
}
10 changes: 5 additions & 5 deletions tests/test_dispute.cairo
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use snforge_std::{
CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_caller_address, declare,
stop_cheat_caller_address, start_cheat_block_timestamp_global, stop_cheat_block_timestamp_global,
cheat_block_timestamp,
CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_block_timestamp, cheat_caller_address,
declare, start_cheat_block_timestamp_global, stop_cheat_block_timestamp_global,
stop_cheat_caller_address,
};
use starkhive_contract::base::types::DisputeStatus;
use starkhive_contract::interfaces::IDispute::{IDisputeDispatcher, IDisputeDispatcherTrait};
use starkhive_contract::base::types::{DisputeStatus};
use starknet::{ContractAddress, contract_address_const, get_block_timestamp};


Expand Down Expand Up @@ -243,4 +243,4 @@ fn test_penalise_false_dispute_wrong_caller() {
let claimant: ContractAddress = contract_address_const::<'claimant'>();
cheat_caller_address(contract_address, claimant, CheatSpan::Indefinite);
dispatcher.penalise_false_dispute(1);
}
}