Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add default proposal parameters and custom proposal configuration #127

Merged
merged 5 commits into from
Aug 26, 2024
Merged
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
10 changes: 0 additions & 10 deletions src/constants.cairo
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
const NEW_PROPOSAL_QUORUM: u128 =
200; // 1/200 of totalSupply required to propose an upgrade. Quorums don't take into account investors. at all, they don't count into total eligible voters, but do vote
const QUORUM: u128 = 10; // 1/10 of totalSupply required to participate to pass
const MINUS_ONE: felt252 = 0x800000000000011000000000000000000000000000000000000000000000000;
const TEAM_TOKEN_BALANCE: u128 = 1000000000000000000;
const PROPOSAL_VOTING_SECONDS: u64 = consteval_int!(60 * 60 * 24 * 7);


// ADDRESSES

const USDC_ADDRESS: felt252 = 0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8;
const ETH_ADDRESS: felt252 = 0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7;
const BTC_ADDRESS: felt252 = 0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac;
50 changes: 34 additions & 16 deletions src/contract.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ mod Governance {
ref self: ContractState,
voting_token_class: ClassHash,
floating_token_class: ClassHash,
treasury_classhash: ClassHash,
recipient: ContractAddress
) {
// This is not used in production on mainnet, because the governance token is already deployed (and distributed).
Expand Down Expand Up @@ -170,25 +171,42 @@ mod Governance {
staking.set_curve_point(SIX_MONTHS, 160);
staking.set_curve_point(ONE_YEAR, 250);

let treasury_classhash = 0x06a38a382eddf3d7ebf9673516ac7cf1ff185a1ecbf490cb07f1687e531eb9ec
.try_into()
.unwrap();
let mut treasury_calldata: Array<felt252> = ArrayTrait::new();
treasury_calldata.append(governance_address.into());
treasury_calldata.append(0x1); // carmine amm addr
treasury_calldata.append(0x1); // zklend addr
let (treasury_address, _) = deploy_syscall(
treasury_classhash, 42, treasury_calldata.span(), true
)
.unwrap();
let proposals = IProposalsDispatcher { contract_address: governance_address };
let send_tokens_custom_proposal_config: CustomProposalConfig = CustomProposalConfig {
target: treasury_address.into(),
selector: selector!("send_tokens_to_address"),
library_call: false

if (treasury_classhash.into() != 0) {
let mut treasury_calldata: Array<felt252> = ArrayTrait::new();
treasury_calldata.append(governance_address.into());
treasury_calldata.append(0x1); // carmine amm addr
treasury_calldata.append(0x1); // zklend addr
let (treasury_address, _) = deploy_syscall(
treasury_classhash, 42, treasury_calldata.span(), true
)
.unwrap();

let send_tokens_custom_proposal_config: CustomProposalConfig = CustomProposalConfig {
target: treasury_address.into(),
selector: selector!("send_tokens_to_address"),
library_call: false,
proposal_voting_time: 0 // use global default
};

proposals.add_custom_proposal_config(send_tokens_custom_proposal_config);
}

let set_default_proposal_params_custom_proposal_config: CustomProposalConfig =
CustomProposalConfig {
target: governance_address.into(),
selector: selector!("set_default_proposal_params"),
library_call: false,
proposal_voting_time: 0 // use global default
};

proposals.add_custom_proposal_config(send_tokens_custom_proposal_config);
proposals.add_custom_proposal_config(set_default_proposal_params_custom_proposal_config);

proposals
.set_default_proposal_params(
quorum: 10, proposal_voting_seconds: consteval_int!(60 * 60 * 24 * 7)
); // can be omitted to keep the default values
}

#[abi(embed_v0)]
Expand Down
47 changes: 43 additions & 4 deletions src/proposals.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ trait IProposals<TContractState> {
);
fn get_total_delegated_to(self: @TContractState, to_addr: ContractAddress) -> u128;
fn add_custom_proposal_config(ref self: TContractState, config: CustomProposalConfig) -> u32;
fn set_default_proposal_params(
ref self: TContractState, quorum: u32, proposal_voting_seconds: u32
);
}

#[starknet::component]
Expand Down Expand Up @@ -94,7 +97,9 @@ mod proposals {
custom_proposal_type: LegacyMap::<u32, CustomProposalConfig>, // custom proposal type
custom_proposal_payload: LegacyMap::<
(u32, u32), felt252
> // mapping from prop_id and index to calldata
>, // mapping from prop_id and index to calldata
quorum: u32,
proposal_voting_seconds: u32
}

#[derive(starknet::Event, Drop)]
Expand Down Expand Up @@ -270,6 +275,24 @@ mod proposals {
};
i
}

fn get_quorum(self: @ComponentState<TContractState>) -> u32 {
let saved = self.quorum.read();
if (saved == 0) {
10
} else {
saved
}
}

fn get_proposal_voting_seconds(self: @ComponentState<TContractState>) -> u64 {
let saved = self.proposal_voting_seconds.read();
if (saved == 0) {
consteval_int!(60 * 60 * 24 * 7)
} else {
saved.into()
}
}
}

#[embeddable_as(ProposalsImpl)]
Expand Down Expand Up @@ -337,7 +360,7 @@ mod proposals {
self.proposal_details.write(prop_id, prop_details);

let current_timestamp: u64 = get_block_timestamp();
let end_timestamp: u64 = current_timestamp + constants::PROPOSAL_VOTING_SECONDS;
let end_timestamp: u64 = current_timestamp + self.get_proposal_voting_seconds();
self.proposal_vote_end_timestamp.write(prop_id, end_timestamp);

self.emit(Proposed { prop_id, payload, to_upgrade });
Expand Down Expand Up @@ -367,7 +390,11 @@ mod proposals {
self.proposal_details.write(prop_id_felt, prop_details);

let current_timestamp: u64 = get_block_timestamp();
let end_timestamp: u64 = current_timestamp + constants::PROPOSAL_VOTING_SECONDS;
let end_timestamp: u64 = if (config.proposal_voting_time == 0) {
current_timestamp + self.get_proposal_voting_seconds()
} else {
current_timestamp + config.proposal_voting_time.into()
};
self.proposal_vote_end_timestamp.write(prop_id_felt, end_timestamp);
self.emit(Proposed { prop_id: prop_id_felt, payload, to_upgrade: 5 });

Expand Down Expand Up @@ -522,7 +549,7 @@ mod proposals {
assert(total_eligible_votes_u256.high == 0, 'unable to check quorum');
let total_eligible_votes: u128 = total_eligible_votes_u256.low;

let quorum_threshold: u128 = total_eligible_votes * constants::QUORUM;
let quorum_threshold: u128 = total_eligible_votes * self.get_quorum().into();
if total_tally_multiplied < quorum_threshold {
return constants::MINUS_ONE; // didn't meet quorum
}
Expand Down Expand Up @@ -554,5 +581,17 @@ mod proposals {
self.custom_proposal_type.write(idx, config);
idx
}

fn set_default_proposal_params(
ref self: ComponentState<TContractState>, quorum: u32, proposal_voting_seconds: u32
) {
assert(get_caller_address() == get_contract_address(), 'can only be called by self');
assert(quorum < 30, 'quorum must be <30');
assert(quorum >= 1, 'quorum < 1?');
assert(proposal_voting_seconds > 3600, 'propvoting secs too short');
assert(proposal_voting_seconds < 3000000, 'propvoting secs > 1mo?');
self.quorum.write(quorum);
self.proposal_voting_seconds.write(proposal_voting_seconds);
}
}
}
3 changes: 2 additions & 1 deletion src/types.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ type ContractType =
struct CustomProposalConfig {
target: felt252, //class hash if library call, contract address if regular call
selector: felt252,
library_call: bool
library_call: bool,
proposal_voting_time: u32
}

#[derive(Drop, Serde, starknet::Store)]
Expand Down
2 changes: 2 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ successfully

[] Re-applying a proposal that has already passed : make sure the contract rejects the submission of a proposal that has expired in the past

[] Change the quorum and proposal voting time


### Delegate_vote and withdraw_delegation functions

Expand Down
2 changes: 1 addition & 1 deletion tests/basic.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ fn test_upgrade_mainnet_to_master() {

//simulate passage of time
let current_timestamp = get_block_timestamp();
let end_timestamp = current_timestamp + constants::PROPOSAL_VOTING_SECONDS;
let end_timestamp = current_timestamp + consteval_int!(60 * 60 * 24 * 7);
start_warp(CheatTarget::One(gov_contract_addr), end_timestamp + 1);

assert(dispatcher.get_proposal_status(new_prop_id) == 1, 'proposal not passed!');
Expand Down
1 change: 1 addition & 0 deletions tests/deploy.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fn test_deploy() {
let mut args: Array<felt252> = ArrayTrait::new();
args.append(voting_token_class);
args.append(floating_token_class);
args.append(0);
args.append(0x03f37e36c20E85e6F39b2C6F6e7ECEB2e3aAb40b94064f20983588cfe9f6fc60);
gov_contract.deploy(@args).expect('unable to deploy governance');
}
13 changes: 7 additions & 6 deletions tests/proposals_tests.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ use super::staking_tests::{set_staking_curve, stake_all, stake_half};


const GOV_TOKEN_INITIAL_SUPPLY: felt252 = 1000000000000000000;

const PROPOSAL_VOTING_SECONDS: u64 = consteval_int!(60 * 60 * 24 * 7);
const QUORUM: u128 = 10;

#[test]
fn test_express_proposal() {
Expand Down Expand Up @@ -62,7 +63,7 @@ fn test_proposal_expiry() {

//simulate passage of time
let current_timestamp = get_block_timestamp();
let end_timestamp = current_timestamp + constants::PROPOSAL_VOTING_SECONDS;
let end_timestamp = current_timestamp + PROPOSAL_VOTING_SECONDS;
start_warp(CheatTarget::One(gov.contract_address), end_timestamp + 1);

let status = dispatcher.get_proposal_status(prop_id);
Expand All @@ -87,7 +88,7 @@ fn test_vote_on_expired_proposal() {

//simulate passage of time
let current_timestamp = get_block_timestamp();
let end_timestamp = current_timestamp + constants::PROPOSAL_VOTING_SECONDS;
let end_timestamp = current_timestamp + PROPOSAL_VOTING_SECONDS;
start_warp(CheatTarget::One(gov.contract_address), end_timestamp + 1);

prank(
Expand Down Expand Up @@ -137,11 +138,11 @@ fn test_vote_on_quorum_not_met() {
}
.totalSupply()
.low;
let quorum_threshold = total_eligible_votes * constants::QUORUM / 100;
let quorum_threshold = total_eligible_votes * QUORUM / 100;

assert(total_votes < quorum_threshold, 'Total votes >= quorum threshold');
let current_timestamp = get_block_timestamp();
let end_timestamp = current_timestamp + constants::PROPOSAL_VOTING_SECONDS;
let end_timestamp = current_timestamp + PROPOSAL_VOTING_SECONDS;
start_warp(CheatTarget::One(gov_contract_addr), end_timestamp + 1);
assert(
dispatcher.get_proposal_status(prop_id) == constants::MINUS_ONE,
Expand Down Expand Up @@ -462,7 +463,7 @@ fn test_add_comment_on_non_live_proposal() {

//simulate passage of time
let current_timestamp = get_block_timestamp();
let end_timestamp = current_timestamp + constants::PROPOSAL_VOTING_SECONDS;
let end_timestamp = current_timestamp + PROPOSAL_VOTING_SECONDS;
start_warp(CheatTarget::One(gov_contract_addr), end_timestamp + 1);

IDiscussionDispatcher { contract_address: gov_contract_addr }
Expand Down
1 change: 1 addition & 0 deletions tests/setup.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ fn deploy_governance_and_both_tokens() -> (
let mut args: Array<felt252> = ArrayTrait::new();
args.append(voting_token_class.class_hash.into());
args.append(floating_token_class.class_hash.into());
args.append(0); // treasury – no treasury
args.append(admin_addr);
gov_contract
.deploy_at(@args, governance_address.try_into().unwrap())
Expand Down
28 changes: 17 additions & 11 deletions tests/test_treasury.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ mod testStorage {
use starknet::ContractAddress;
const zero_address: felt252 = 0;
const GOV_CONTRACT_ADDRESS: felt252 =
0x0304256e5fade73a6fc8f49ed7c1c43ac34e6867426601b01204e1f7ba05b53d;
0x57dfabb5a506bfd1937062562a1adf45c7c4c62d0377ccfc59a0b42d7ab3212;
const CARMINE_AMM_CONTRACT_ADDRESS: felt252 =
0x047472e6755afc57ada9550b6a3ac93129cc4b5f98f51c73e0644d129fd208d9;
const ZKLEND_MARKET_C0NTRACT_ADDRESS: felt252 =
Expand All @@ -43,23 +43,29 @@ fn get_important_addresses() -> (
testStorage::ZKLEND_MARKET_C0NTRACT_ADDRESS
.try_into()
.unwrap();
let contract = declare("Treasury").expect('unable to declare');
let mut calldata = ArrayTrait::new();
gov_contract_address.serialize(ref calldata);
AMM_contract_address.serialize(ref calldata);
zklend_market_contract_address.serialize(ref calldata);

// Precalculate the address to obtain the contract address before the constructor call (deploy) itself
let contract_address = contract.precalculate_address(@calldata);

prank(CheatTarget::One(contract_address), gov_contract_address, CheatSpan::TargetCalls(1));
let (deployed_contract, _) = contract.deploy(@calldata).unwrap();
//let contract = declare("Treasury").expect('unable to declare');
let treasury_address: ContractAddress = match declare("Treasury") {
Result::Ok(r) => {
let contract_address = r.precalculate_address(@calldata);
prank(
CheatTarget::One(contract_address), gov_contract_address, CheatSpan::TargetCalls(1)
);
let (deployed_contract, _) = r.deploy(@calldata).unwrap();
deployed_contract
},
// FIXME – this is suboptimal, but afaik no way to get this in current snforge version?
Result::Err(_) => 0x04c990da03da72bdfb10db5c04e8aaa9d5404a07fe454037facb7744c132d42c
.try_into()
.unwrap()
};

return (
gov_contract_address,
AMM_contract_address,
deployed_contract,
zklend_market_contract_address
gov_contract_address, AMM_contract_address, treasury_address, zklend_market_contract_address
);
}

Expand Down
Loading