-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Add custom proposal implementation Lacks setting of custom proposal configuration during deployment * Add arbitrary proposal * Polish apply_passed_proposal * Polish scarb fmt * Cherry pick setup.cairo from 1fab54f * Move test setup to src/ * Remove unrelated functions from setup.cairo * Fix setup * Polish tests * Add airdrop tests * Adding setup file with generic function which applies a proposal to upgrade root and passes it * Setup environment to deploy governance, deploy gov token and distribute to sample addresses and then vote on update root proposal * Add tests on proposals * fix some issues on proposal tests * Proposals Frontend (#66) * add: basic ui displaying proposals * feat: voting invocations * feat: ✨ new proposal form * feat: ✨ use async for proposal creation * fix: 🐛 bad voting code * chore: 🗑️ cleanup * feat: ✨ add logo * fix: 🐛 wrong site title --------- Co-authored-by: Tomáš Hobza <[email protected]> * Update README.md * Update README.md * feat: default to_upgrade value (#69) Co-authored-by: Tomáš Hobza <[email protected]> * Telegram Notification bot for proposal (#71) * initial commit * change to testnet * Add basic working event printing * notification telegram bot working * add gitignore * Remove node_modules folder and update .gitignore * update config.toml * removing unused address * Delete accidentally committed tests/proposal.cairo * code review changes * rest of the code-review changes * Update packages according to code review feedback * Add error print when unable to send messages to Telegram * Add automatic retrieval of chain id * Polish formatting * Refactor config file reading * code-review changes in index.ts, package-lock * Add CI runner * Trigger CI * Fix CI * Fix CI * Fix CI * Fix CI --------- Co-authored-by: Ondřej Sojka <[email protected]> Co-authored-by: Ondřej Sojka <[email protected]> * Adding proposals tests * Comment code for airdrop_tests and remove old setup file * Add minor fixes towards compilability * Fix all proposals tests * Fix formatting * Proposals Frontend (#66) * add: basic ui displaying proposals * feat: voting invocations * feat: ✨ new proposal form * feat: ✨ use async for proposal creation * fix: 🐛 bad voting code * chore: 🗑️ cleanup * feat: ✨ add logo * fix: 🐛 wrong site title --------- Co-authored-by: Tomáš Hobza <[email protected]> * feat: default to_upgrade value (#69) Co-authored-by: Tomáš Hobza <[email protected]> * Update frontend domain to konoha.vote * Rename package from 'governance' to 'konoha' * Add contributor guidelines * Update README.md with contributing info * Update CONTRIBUTING.md * Add mdbook scaffolding (#75) * Add mdbook stub * Update license to Apache 2.0 * Add some docs * Add custom proposal implementation (#64) * Add custom proposal implementation Lacks setting of custom proposal configuration during deployment * Add arbitrary proposal * Polish apply_passed_proposal * Polish scarb fmt * Cherry pick setup.cairo from 1fab54f * Move test setup to src/ * Remove unrelated functions from setup.cairo * Fix setup * Polish tests * Fix import naming * Add addition of custom proposal * Remove mentions of Carmine in this repository (#73) * Update Scarb.toml, add [lib] * Add custom proposal implementation Lacks setting of custom proposal configuration during deployment * Add arbitrary proposal * Polish apply_passed_proposal * Polish scarb fmt * Cherry pick setup.cairo from 1fab54f * Move test setup to src/ * Remove unrelated functions from setup.cairo * Fix setup * Polish tests * Fix import naming * Add addition of custom proposal * Remove part of Carmine code * Remove rest of Carmine stuff * Polish with scarb fmt * Bump version to 0.4.0 * Fix imports in tests * Remove unused test from lib.cairo * Remove cubit as dependency * Update package description * Update error message to not mention CARM * Move snforge_std to dev-dependency * Update snforge_std to v0.23.0 * Update snforge in CI * Trigger CI * Format * Rename governance:: to konoha:: * Fix proposals tests and mint tokens to admin addr before distributing them in deploy token function * Add quorum tests * Move setup to testing, fix tests so they compile * Merge branch master --------- Co-authored-by: Ondřej Sojka <[email protected]> Co-authored-by: Tomáš Hobza <[email protected]> Co-authored-by: Tomáš Hobza <[email protected]> Co-authored-by: Ondřej Sojka <[email protected]> Co-authored-by: xkrivan5 <[email protected]>
- Loading branch information
1 parent
519f052
commit 802bbef
Showing
7 changed files
with
345 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// use core::hash::HashStateExTrait; | ||
// use core::{ArrayTrait, SpanTrait}; | ||
// use core::debug::PrintTrait; | ||
// use governance::airdrop::{airdrop, IAirdropDispatcher, IAirdropDispatcherTrait}; | ||
// use airdrop::STRK_ADDRESS; | ||
// use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; | ||
// use snforge_std::{ContractClassTrait, declare, start_prank, CheatTarget}; | ||
// use starknet::{ContractAddress, deploy_syscall}; | ||
|
||
// const ADMIN_ADDR: felt252 = 0x42; | ||
// const CLAIMEE_1: felt252 = 0x13; | ||
// const CLAIMEE_2: felt252 = 0x14; | ||
|
||
// fn deploy() -> IAirdropDispatcher { | ||
// let mut calldata = ArrayTrait::new(); | ||
// calldata.append(ADMIN_ADDR); | ||
|
||
// let contract = declare('Airdrop'); | ||
// let address = contract.deploy().expect('unable to deploy Airdrop'); | ||
// IAirdropDispatcher { contract_address: address } | ||
// } | ||
|
||
// #[test] | ||
// fn test_claim_twice_with_same_proof() { | ||
// let airdrop_contract = deploy(); | ||
// let token_contract = deploy_token(airdrop_contract.contract_address); | ||
|
||
// start_prank( | ||
// CheatTarget::One(airdrop_contract.contract_address), ADMIN_ADDR.try_into().unwrap() | ||
// ); | ||
// airdrop_contract.add_root(valid_root); | ||
|
||
// start_prank(CheatTarget::One(airdrop_contract.contract_address), CLAIMEE_1.try_into().unwrap()); | ||
// let initial_proof = array![valid_proof_element]; | ||
// airdrop_contract.claim(CLAIMEE_1, valid_claim_amount, initial_proof.span()); | ||
// assert( | ||
// token_contract.balance_of(CLAIMEE_1.try_into().unwrap()) == valid_claim_amount, | ||
// "First claim failed" | ||
// ); | ||
|
||
// airdrop_contract.claim(CLAIMEE_1, valid_claim_amount, initial_proof.span()); | ||
// assert( | ||
// token_contract.balance_of(CLAIMEE_1.try_into().unwrap()) == valid_claim_amount, | ||
// "Second claim modified the claimee's balance" | ||
// ); | ||
// } | ||
|
||
// #[test] | ||
// #[should_panic(expected: ('INVALID PROOF',))] | ||
// fn test_claim_invalid_proof() { | ||
// let contract = deploy(); | ||
// deploy_token(contract.contract_address); | ||
// start_prank(CheatTarget::One(contract.contract_address), ADMIN_ADDR.try_into().unwrap()); | ||
// contract.add_root(0xf7c8d3f309262572ad35df8ff6c33f24d8114c60eac3bc27bf42382ca82faf); | ||
|
||
// start_prank(CheatTarget::One(contract.contract_address), CLAIMEE_1.try_into().unwrap()); | ||
// let proof = array![0x2a18afb0550a011d54ca3940648e59894c06e4c3d0a611256c0b575bd528b3b, 0x1]; | ||
// contract.claim(0x88, proof.span()); | ||
// } | ||
|
||
// #[test] | ||
// fn test_update_root_and_claim_attempts() { | ||
// let contract = deploy(); | ||
// let tok = deploy_token(contract.contract_address); | ||
// start_prank(CheatTarget::One(contract.contract_address), ADMIN_ADDR.try_into().unwrap()); | ||
|
||
// // add intial root and valid claim | ||
// contract.add_root(initial_root); | ||
// start_prank(CheatTarget::One(contract.contract_address), CLAIMEE_1.try_into().unwrap()); | ||
// contract.claim(claim_amount, valid_proof_for_initial_root.span()); | ||
// assert(tok.balance_of(CLAIMEE_1.try_into().unwrap()) == claim_amount, 'initial claim failed'); | ||
|
||
// // update root | ||
// start_prank(CheatTarget::One(contract.contract_address), ADMIN_ADDR.try_into().unwrap()); | ||
// contract.add_root(new_root); | ||
|
||
// // claim with old root + new proof, should fail | ||
// start_prank(CheatTarget::One(contract.contract_address), CLAIMEE_2.try_into().unwrap()); | ||
// contract.claim(claim_amount, new_proof_not_matching_old_root.span()); | ||
// // check fail : to do, use should panic ? | ||
|
||
// // claim with new root + old proof, should fail | ||
// contract.claim(claim_amount, old_proof_not_matching_new_root.span()); | ||
// // check fail : to do | ||
// } | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
mod vesting; | ||
mod basic; | ||
mod test_treasury; | ||
mod proposals_tests; | ||
mod airdrop_tests; | ||
mod setup; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
use array::ArrayTrait; | ||
use core::traits::TryInto; | ||
use debug::PrintTrait; | ||
use starknet::ContractAddress; | ||
use openzeppelin::token::erc20::interface::{ | ||
IERC20Dispatcher, IERC20DispatcherTrait, IERC20CamelOnlyDispatcher, | ||
IERC20CamelOnlyDispatcherTrait | ||
}; | ||
use snforge_std::{ | ||
BlockId, declare, ContractClassTrait, ContractClass, start_prank, start_warp, CheatTarget, | ||
prank, CheatSpan | ||
}; | ||
|
||
use super::setup::{ | ||
admin_addr, first_address, second_address, deploy_governance, deploy_and_distribute_gov_tokens, | ||
test_vote_upgrade_root, check_if_healthy | ||
}; | ||
use konoha::contract::IGovernanceDispatcher; | ||
use konoha::contract::IGovernanceDispatcherTrait; | ||
use konoha::proposals::IProposalsDispatcher; | ||
use konoha::proposals::IProposalsDispatcherTrait; | ||
use konoha::upgrades::IUpgradesDispatcher; | ||
use konoha::upgrades::IUpgradesDispatcherTrait; | ||
use konoha::constants; | ||
use starknet::get_block_timestamp; | ||
|
||
|
||
const GOV_TOKEN_INITIAL_SUPPLY: felt252 = 1000000000000000000; | ||
|
||
|
||
fn test_express_proposal() { | ||
let token_contract = deploy_and_distribute_gov_tokens(admin_addr.try_into().unwrap()); | ||
let gov_contract = deploy_governance(token_contract.contract_address); | ||
let gov_contract_addr = gov_contract.contract_address; | ||
|
||
let dispatcher = IProposalsDispatcher { contract_address: gov_contract_addr }; | ||
|
||
start_prank(CheatTarget::One(gov_contract_addr), admin_addr.try_into().unwrap()); | ||
let prop_id = dispatcher.submit_proposal(42, 1); | ||
|
||
start_prank(CheatTarget::One(gov_contract_addr), admin_addr.try_into().unwrap()); | ||
dispatcher.vote(prop_id, 1); | ||
|
||
assert!(dispatcher.get_proposal_status(prop_id) == 1, "proposal not passed!"); | ||
} | ||
|
||
#[test] | ||
fn test_proposal_expiry() { | ||
let token_contract = deploy_and_distribute_gov_tokens(admin_addr.try_into().unwrap()); | ||
let gov_contract = deploy_governance(token_contract.contract_address); | ||
let gov_contract_addr = gov_contract.contract_address; | ||
|
||
let dispatcher = IProposalsDispatcher { contract_address: gov_contract_addr }; | ||
|
||
start_prank(CheatTarget::One(gov_contract_addr), admin_addr.try_into().unwrap()); | ||
let prop_id = dispatcher.submit_proposal(42, 1); | ||
|
||
//simulate passage of time | ||
let current_timestamp = get_block_timestamp(); | ||
let end_timestamp = current_timestamp + constants::PROPOSAL_VOTING_SECONDS; | ||
start_warp(CheatTarget::One(gov_contract_addr), end_timestamp + 1); | ||
|
||
let status = dispatcher.get_proposal_status(prop_id); | ||
assert!(status == constants::MINUS_ONE, "proposal not expired!"); | ||
} | ||
|
||
#[test] | ||
#[should_panic(expected: ('voting concluded',))] | ||
fn test_vote_on_expired_proposal() { | ||
let token_contract = deploy_and_distribute_gov_tokens(admin_addr.try_into().unwrap()); | ||
let gov_contract = deploy_governance(token_contract.contract_address); | ||
let gov_contract_addr = gov_contract.contract_address; | ||
|
||
let dispatcher = IProposalsDispatcher { contract_address: gov_contract_addr }; | ||
|
||
start_prank(CheatTarget::One(gov_contract_addr), admin_addr.try_into().unwrap()); | ||
let prop_id = dispatcher.submit_proposal(42, 1); | ||
|
||
//simulate passage of time | ||
let current_timestamp = get_block_timestamp(); | ||
let end_timestamp = current_timestamp + constants::PROPOSAL_VOTING_SECONDS; | ||
start_warp(CheatTarget::One(gov_contract_addr), end_timestamp + 1); | ||
|
||
prank( | ||
CheatTarget::One(token_contract.contract_address), | ||
admin_addr.try_into().unwrap(), | ||
CheatSpan::TargetCalls(1) | ||
); | ||
token_contract.transfer(first_address.try_into().unwrap(), 100000.try_into().unwrap()); | ||
start_prank(CheatTarget::One(gov_contract_addr), first_address.try_into().unwrap()); | ||
dispatcher.vote(prop_id, 1); | ||
} | ||
|
||
#[test] | ||
fn test_vote_on_quorum_not_met() { | ||
let token_contract = deploy_and_distribute_gov_tokens(admin_addr.try_into().unwrap()); | ||
let gov_contract = deploy_governance(token_contract.contract_address); | ||
let gov_contract_addr = gov_contract.contract_address; | ||
|
||
let dispatcher = IProposalsDispatcher { contract_address: gov_contract_addr }; | ||
|
||
prank( | ||
CheatTarget::One(gov_contract_addr), | ||
admin_addr.try_into().unwrap(), | ||
CheatSpan::TargetCalls(1) | ||
); | ||
let prop_id = dispatcher.submit_proposal(42, 1); | ||
|
||
prank( | ||
CheatTarget::One(token_contract.contract_address), | ||
admin_addr.try_into().unwrap(), | ||
CheatSpan::TargetCalls(1) | ||
); | ||
token_contract.transfer(first_address.try_into().unwrap(), 100000.try_into().unwrap()); | ||
|
||
prank( | ||
CheatTarget::One(gov_contract_addr), | ||
first_address.try_into().unwrap(), | ||
CheatSpan::TargetCalls(1) | ||
); | ||
dispatcher.vote(prop_id, 1); | ||
|
||
let (yay_votes, nay_votes) = dispatcher.get_vote_counts(prop_id); | ||
let total_votes = yay_votes + nay_votes; | ||
let total_eligible_votes: u128 = IERC20CamelOnlyDispatcher { | ||
contract_address: token_contract.contract_address | ||
} | ||
.totalSupply() | ||
.low; | ||
let quorum_threshold = total_eligible_votes * constants::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; | ||
start_warp(CheatTarget::One(gov_contract_addr), end_timestamp + 1); | ||
assert( | ||
dispatcher.get_proposal_status(prop_id) == constants::MINUS_ONE, | ||
'Proposal pass & quorum not met' | ||
); | ||
} | ||
|
||
#[test] | ||
#[should_panic(expected: ('not enough tokens to submit',))] | ||
fn test_submit_proposal_under_quorum() { | ||
let token_contract = deploy_and_distribute_gov_tokens(admin_addr.try_into().unwrap()); | ||
let gov_contract = deploy_governance(token_contract.contract_address); | ||
let gov_contract_addr = gov_contract.contract_address; | ||
|
||
let dispatcher = IProposalsDispatcher { contract_address: gov_contract_addr }; | ||
|
||
prank( | ||
CheatTarget::One(token_contract.contract_address), | ||
admin_addr.try_into().unwrap(), | ||
CheatSpan::TargetCalls(1) | ||
); | ||
token_contract.transfer(first_address.try_into().unwrap(), 100000.try_into().unwrap()); | ||
|
||
prank( | ||
CheatTarget::One(gov_contract_addr), | ||
first_address.try_into().unwrap(), | ||
CheatSpan::TargetCalls(1) | ||
); | ||
dispatcher.submit_proposal(42, 1); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
use core::traits::Into; | ||
use array::ArrayTrait; | ||
use core::traits::TryInto; | ||
use debug::PrintTrait; | ||
use starknet::ContractAddress; | ||
use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; | ||
use snforge_std::{ | ||
BlockId, declare, ContractClassTrait, ContractClass, start_prank, start_warp, CheatTarget | ||
}; | ||
use core::ResultTrait; | ||
|
||
|
||
use konoha::contract::IGovernanceDispatcher; | ||
use konoha::contract::IGovernanceDispatcherTrait; | ||
use konoha::proposals::IProposalsDispatcher; | ||
use konoha::proposals::IProposalsDispatcherTrait; | ||
use konoha::upgrades::IUpgradesDispatcher; | ||
use konoha::upgrades::IUpgradesDispatcherTrait; | ||
use konoha::constants; | ||
use openzeppelin::token::erc20::interface::IERC20; | ||
use starknet::get_block_timestamp; | ||
|
||
|
||
const GOV_TOKEN_INITIAL_SUPPLY: u256 = 1000000000000000000; | ||
|
||
const first_address: felt252 = 0x1; | ||
const second_address: felt252 = 0x2; | ||
const admin_addr: felt252 = 0x3; | ||
|
||
fn deploy_governance(token_address: ContractAddress) -> IGovernanceDispatcher { | ||
let gov_contract = declare("Governance").expect('unable to declare governance'); | ||
let mut args: Array<felt252> = ArrayTrait::new(); | ||
args.append(token_address.into()); | ||
let (address, _) = gov_contract.deploy(@args).expect('unable to deploy governance'); | ||
IGovernanceDispatcher { contract_address: address } | ||
} | ||
|
||
|
||
fn deploy_and_distribute_gov_tokens(recipient: ContractAddress) -> IERC20Dispatcher { | ||
let mut calldata = ArrayTrait::new(); | ||
calldata.append(GOV_TOKEN_INITIAL_SUPPLY.low.into()); | ||
calldata.append(GOV_TOKEN_INITIAL_SUPPLY.high.into()); | ||
calldata.append(recipient.into()); | ||
|
||
let gov_token_contract = declare("FloatingToken").expect('unable to declare FloatingToken'); | ||
let (token_addr, _) = gov_token_contract | ||
.deploy(@calldata) | ||
.expect('unable to deploy FloatingToken'); | ||
IERC20Dispatcher { contract_address: token_addr } | ||
} | ||
|
||
|
||
fn test_vote_upgrade_root(new_merkle_root: felt252) { | ||
let token_contract = deploy_and_distribute_gov_tokens(admin_addr.try_into().unwrap()); | ||
let gov_contract = deploy_governance(token_contract.contract_address); | ||
let gov_contract_addr = gov_contract.contract_address; | ||
|
||
let dispatcher = IProposalsDispatcher { contract_address: gov_contract_addr }; | ||
|
||
start_prank(CheatTarget::One(gov_contract_addr), admin_addr.try_into().unwrap()); | ||
let prop_id = dispatcher.submit_proposal(new_merkle_root, 3); | ||
|
||
start_prank(CheatTarget::One(gov_contract_addr), first_address.try_into().unwrap()); | ||
dispatcher.vote(prop_id, 1); | ||
start_prank(CheatTarget::One(gov_contract_addr), second_address.try_into().unwrap()); | ||
dispatcher.vote(prop_id, 1); | ||
start_prank(CheatTarget::One(gov_contract_addr), admin_addr.try_into().unwrap()); | ||
dispatcher.vote(prop_id, 1); | ||
|
||
assert(dispatcher.get_proposal_status(prop_id) == 1, 'proposal not passed!'); | ||
|
||
let upgrade_dispatcher = IUpgradesDispatcher { contract_address: gov_contract_addr }; | ||
upgrade_dispatcher.apply_passed_proposal(prop_id); | ||
assert(check_if_healthy(gov_contract_addr), 'new gov not healthy'); | ||
} | ||
|
||
fn check_if_healthy(gov_contract_addr: ContractAddress) -> bool { | ||
// TODO | ||
let dispatcher = IProposalsDispatcher { contract_address: gov_contract_addr }; | ||
dispatcher.get_proposal_status(0); | ||
let prop_details = dispatcher.get_proposal_details(0); | ||
(prop_details.payload + prop_details.to_upgrade) != 0 | ||
} |