Skip to content

Commit

Permalink
Feat/init aa dao (#468)
Browse files Browse the repository at this point in the history
* start init voting in dao AA

* fix feed data

* wip vote + hotfix feed
  • Loading branch information
MSghais authored Feb 5, 2025
1 parent 0c95735 commit f6936fe
Show file tree
Hide file tree
Showing 5 changed files with 400 additions and 21 deletions.
3 changes: 2 additions & 1 deletion apps/mobile/src/screens/Feed/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ export const Feed: React.FC<FeedScreenProps> = ({navigation}) => {
// </>
// }
contentContainerStyle={styles.flatListContent}
data={filteredNotes}
data={feedData}
// data={filteredNotes}
keyExtractor={(item) => item?.id}
renderItem={({item}) => {
if (item.kind === NDKKind.ChannelCreation || item.kind === NDKKind.ChannelMetadata) {
Expand Down
162 changes: 162 additions & 0 deletions onchain/cairo/afk/src/components/voting_proposal.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use afk::types::defi_types::{TokenPermitted, DepositUser, MintDepositEvent, WithdrawDepositEvent};
use starknet::ContractAddress;

// TODO
// Create the as a Vault component
#[starknet::component]
pub mod VoteComponent {
use afk::interfaces::erc20_mintable::{IERC20MintableDispatcher, IERC20MintableDispatcherTrait};
use afk::interfaces::voting_proposal::{IVoteProposal, Proposal, ProposalStatus, ProposalType, UserVote, VoteState};
use afk::tokens::erc20::{ERC20, IERC20, IERC20Dispatcher, IERC20DispatcherTrait};
use afk::types::constants::{MINTER_ROLE, ADMIN_ROLE};
use core::num::traits::Zero;

use openzeppelin::access::accesscontrol::AccessControlComponent;
use openzeppelin::introspection::src5::SRC5Component;
use starknet::event::EventEmitter;
use starknet::storage::{
Map, StorageMapReadAccess, StorageMapWriteAccess, // Stor
StoragePointerReadAccess,StoragePointerWriteAccess,
StoragePathEntry,
// MutableEntryStoragePathEntry, StorableEntryReadAccess, StorageAsPathReadForward,
// MutableStorableEntryReadAccess, MutableStorableEntryWriteAccess,
// StorageAsPathWriteForward,PathableStorageEntryImpl
};

use starknet::{
ContractAddress, get_caller_address, contract_address_const, get_block_timestamp,
get_contract_address, ClassHash
};
use super::{DepositUser, TokenPermitted, MintDepositEvent, WithdrawDepositEvent};


component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);
component!(path: SRC5Component, storage: src5, event: SRC5Event);

// AccessControl
#[abi(embed_v0)]
impl AccessControlImpl =
AccessControlComponent::AccessControlImpl<ComponentState<TContractState>>;
impl AccessControlInternalImpl = AccessControlComponent::InternalImpl<ComponentState<TContractState>>;

#[storage]
struct Storage {
proposals: Map<u256, Proposal>,
proposal_by_user: Map<ContractAddress, u256>,
total_proposal:u256,
vote_state_by_proposal: Map<u256, VoteState>,
vote_by_proposal: Map<u256, Proposal>,
#[substorage(v0)]
accesscontrol: AccessControlComponent::Storage,
#[substorage(v0)]
src5: SRC5Component::Storage,
}


#[constructor]
fn constructor(
ref self: ComponentState<TContractState>, token_address: ContractAddress, admin: ContractAddress
) {
// Give MINTER role to the Vault for the token used
self.total_proposal.write(0);
// self.token_address.write(token_address);
// self.accesscontrol.initializer();
// self.accesscontrol._grant_role(ADMIN_ROLE, admin);
// self.accesscontrol._grant_role(MINTER_ROLE, admin);
}

#[event]
#[derive(Drop, starknet::Event)]
pub enum Event {
MintDepositEvent: MintDepositEvent,
WithdrawDepositEvent: WithdrawDepositEvent,
#[flat]
AccessControlEvent: AccessControlComponent::Event,
#[flat]
SRC5Event: SRC5Component::Event,
}

#[embeddable_as(Vote)]
impl VoteImpl<
TContractState, +HasComponent<TContractState>
> of super::IVoteProposal<ComponentState<TContractState>> {

fn create_proposal(ref self: ComponentState<TContractState>, proposal:Proposal) {

let caller = get_caller_address();
let proposal_id = self.total_proposal.read();
self.total_proposal.write(proposal_id + 1);
self.proposals.entry(proposal_id).write(proposal);

let vote_state = VoteState {
votes_by_proposal: Map::new(),
user_votes: Map::new(),
has_voted: Map::new(),
};
self.proposal_by_user.entry(caller).write(proposal_id);
self.vote_state_by_proposal.entry(proposal_id).write(vote_state);
}

fn cast_vote_type(ref self: ComponentState<TContractState>, proposal_id: u256, vote: UserVote) {
let caller = get_caller_address();
self.vote_by_proposal.entry(proposal_id).write(vote);
self.user_votes.entry(caller).write(proposal_id);
self.has_voted.entry(caller).write(true);
self.user_vote_type.entry(caller).write(vote);
}

fn cast_vote(ref self: ComponentState<TContractState>, proposal_id: u256, vote: u64) {
let caller = get_caller_address();
self.vote_by_proposal.entry(proposal_id).write(vote);
self.user_votes.entry(caller).write(proposal_id);
self.has_voted.entry(caller).write(true);

let mut vote_state = self.vote_state_by_proposal.read(proposal_id);

vote_state.user_votes.entry(caller).write(vote);
vote_state.has_voted.entry(caller).write(true);
// vote_state.votes_by_proposal.entry(vote).write(vote_state.votes_by_proposal.read(vote) + 1);

self.vote_state_by_proposal.entry(proposal_id).write(vote_state);
self.user_vote_type.entry(caller).write(vote);
}

fn get_vote_state(ref self: ComponentState<TContractState>, proposal_id: u256) -> VoteState {
let caller = get_caller_address();
self.vote_by_proposal.read(proposal_id)
}

fn get_proposal(ref self: ComponentState<TContractState>, proposal_id: u256) -> Proposal {
let caller = get_caller_address();
self.proposals.read(proposal_id)
}

fn get_user_vote(ref self: ComponentState<TContractState>, proposal_id: u256, user:ContractAddress) -> UserVote {
let caller = get_caller_address();
self.vote_by_proposal.read(proposal_id)
}



}
// Admin
// Add OPERATOR role to the Vault escrow
// #[external(v0)]
// fn set_control_role(
// ref self: TContractState, recipient: ContractAddress, role: felt252, is_enable: bool
// ) {
// self.accesscontrol.assert_only_role(ADMIN_ROLE);
// assert!(
// role == ADMIN_ROLE
// || role == OPERATOR_ROLE // Think and Add others roles needed on the protocol
// ,
// "role not enable"
// );
// if is_enable {
// self.accesscontrol._grant_role(role, recipient);
// } else {
// self.accesscontrol._revoke_role(role, recipient);
// }
// }

}
138 changes: 118 additions & 20 deletions onchain/cairo/afk/src/dao/dao_aa.cairo
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use starknet::account::Call;
// use starknet::{ContractAddress, get_caller_address, get_contract_address,
// contract_address_const};
use super::profile::NostrProfile;
use super::request::SocialRequest;
use super::transfer::Transfer;

use afk::profile::NostrProfile;
use afk::social::request::SocialRequest;
use afk::tokens::transfer::Transfer;

#[starknet::interface]
pub trait IDaoAA<TContractState> {
Expand Down Expand Up @@ -35,40 +34,77 @@ pub mod DaoAA {
use openzeppelin_access::accesscontrol::AccessControlComponent;
use openzeppelin_governance::timelock::TimelockControllerComponent;
use openzeppelin_introspection::src5::SRC5Component;
use starknet::ContractAddress;
use starknet::account::Call;
use afk::interfaces::voting::{IVoteProposal, Proposal, ProposalStatus, ProposalType, UserVote, VoteState};

use starknet::storage::{
StoragePointerReadAccess, StoragePointerWriteAccess, StoragePathEntry, Map
};
use starknet::{get_caller_address, get_contract_address, get_tx_info, ContractAddress};
use super::ISRC6;

use super::super::request::{
SocialRequest, SocialRequestImpl, SocialRequestTrait, Encode, Signature
use afk::bip340::{Signature, SchnorrSignature};
use afk::social::request::{
SocialRequest, SocialRequestImpl, SocialRequestTrait, Encode
};
use super::super::transfer::Transfer;
use afk::social::transfer::Transfer;
use super::{IDaoAADispatcher, IDaoAADispatcherTrait};

component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent);
component!(path: TimelockControllerComponent, storage: timelock, event: TimelockEvent);
// component!(path: TimelockControllerComponent, storage: timelock, event: TimelockEvent);
component!(path: SRC5Component, storage: src5, event: SRC5Event);

// Timelock Mixin
// AccessControl
#[abi(embed_v0)]
impl AccessControlImpl =
AccessControlComponent::AccessControlImpl<ContractState>;
impl AccessControlInternalImpl = AccessControlComponent::InternalImpl<ContractState>;

// Upgradeable
impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl<ContractState>;

// SRC5
#[abi(embed_v0)]
impl TimelockMixinImpl =
TimelockControllerComponent::TimelockMixinImpl<ContractState>;
impl TimelockInternalImpl = TimelockControllerComponent::InternalImpl<ContractState>;
impl SRC5Impl = SRC5Component::SRC5Impl<ContractState>;

// // Timelock Mixin
// #[abi(embed_v0)]
// impl TimelockMixinImpl =
// TimelockControllerComponent::TimelockMixinImpl<ContractState>;
// impl TimelockInternalImpl = TimelockControllerComponent::InternalImpl<ContractState>;

#[storage]
struct Storage {
#[key]
public_key: u256,
transfers: Map<u256, bool>,
proposals: Map<u256, Proposal>,
proposal_by_user: Map<ContractAddress, u256>,
total_proposal:u256,
vote_state_by_proposal: Map<u256, VoteState>,
vote_by_proposal: Map<u256, Proposal>,
// votes_by_proposal: Map<u256, u256>, // Maps proposal ID to vote count
user_votes: Map<(u256, ContractAddress), u64>, // Maps user address to proposal ID they voted for
has_voted: Map<(u256, ContractAddress), bool>,
user_vote_type: Map<(u256, ContractAddress), UserVote>,
#[substorage(v0)]
accesscontrol: AccessControlComponent::Storage,
#[substorage(v0)]
src5: SRC5Component::Storage,
#[substorage(v0)]
upgradeable: UpgradeableComponent::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
AccountCreated: AccountCreated,
#[flat]
AccessControlEvent: AccessControlComponent::Event,
#[flat]
SRC5Event: SRC5Component::Event,
#[flat]
UpgradeableEvent: UpgradeableComponent::Event
}

#[derive(Drop, starknet::Event)]
Expand All @@ -80,6 +116,10 @@ pub mod DaoAA {
#[constructor]
fn constructor(ref self: ContractState, public_key: u256) {
self.public_key.write(public_key);
self.total_proposal.write(0);
// self.accesscontrol.initializer();
// self.accesscontrol._grant_role(ADMIN_ROLE, admin);
// self.accesscontrol._grant_role(MINTER_ROLE, admin);
self.emit(AccountCreated { public_key: public_key });
}

Expand All @@ -104,13 +144,71 @@ pub mod DaoAA {
"wrong recipient"
);

if let Option::Some(id) = request.verify() {
assert!(!self.transfers.read(id), "double spend");
self.transfers.entry(id).write(true);
erc20.transfer(request.content.recipient_address, request.content.amount);
} else {
panic!("can't verify signature");
}
// if let Option::Some(id) = request.verify() {
// assert!(!self.transfers.read(id), "double spend");
// self.transfers.entry(id).write(true);
// erc20.transfer(request.content.recipient_address, request.content.amount);
// } else {
// panic!("can't verify signature");
// }
}
}

#[abi(embed_v0)]
impl DaoAA of super::IVoteProposal<ContractState> {
fn create_proposal(ref self: ContractState, proposal:Proposal) {

let caller = get_caller_address();
let proposal_id = self.total_proposal.read();
self.total_proposal.write(proposal_id + 1);
self.proposals.entry(proposal_id).write(proposal);

let vote_state = VoteState {
votes_by_proposal: Map::new(),
user_votes: Map::new(),
has_voted: Map::new(),
};
self.proposal_by_user.entry(caller).write(proposal_id);
self.vote_state_by_proposal.entry(proposal_id).write(vote_state);
}

fn cast_vote_type(ref self: ContractState, proposal_id: u256, vote: UserVote) {
let caller = get_caller_address();
self.vote_by_proposal.entry(proposal_id).write(vote);
self.user_votes.entry(caller).write(proposal_id);
self.has_voted.entry(caller).write(true);
self.user_vote_type.entry(caller).write(vote);
}

fn cast_vote(ref self: ContractState, proposal_id: u256, vote: u64) {
let caller = get_caller_address();
self.vote_by_proposal.entry(proposal_id).write(vote);
self.user_votes.entry(caller).write(proposal_id);
self.has_voted.entry(caller).write(true);

let mut vote_state = self.vote_state_by_proposal.read(proposal_id);

vote_state.user_votes.entry(caller).write(vote);
vote_state.has_voted.entry(caller).write(true);
// vote_state.votes_by_proposal.entry(vote).write(vote_state.votes_by_proposal.read(vote) + 1);

self.vote_state_by_proposal.entry(proposal_id).write(vote_state);
self.user_vote_type.entry(caller).write(vote);
}

// fn get_vote_state(ref self: ContractState, proposal_id: u256) -> VoteState {
// let caller = get_caller_address();
// self.vote_by_proposal.read(proposal_id)
// }

fn get_proposal(ref self: ContractState, proposal_id: u256) -> Proposal {
let caller = get_caller_address();
self.proposals.read(proposal_id)
}

fn get_user_vote(ref self: ContractState, proposal_id: u256, user:ContractAddress) -> UserVote {
let caller = get_caller_address();
self.vote_by_proposal.read(proposal_id)
}
}

Expand Down
Loading

0 comments on commit f6936fe

Please sign in to comment.