Skip to content

Commit

Permalink
Implement tests according to missing test list #58 (#61)
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
6 people committed Jun 8, 2024
1 parent 519f052 commit 802bbef
Show file tree
Hide file tree
Showing 7 changed files with 345 additions and 7 deletions.
7 changes: 7 additions & 0 deletions Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag

[lib]


# can be fixed by doing import super::testing from tests
[dev-dependencies]
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.23.0" }

[lib]

[[target.starknet-contract]]

[scripts]
Expand Down
5 changes: 0 additions & 5 deletions src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,11 @@ mod treasury_types {
mod constants;
mod contract;
mod merkle_tree;
//mod options;
mod proposals;
mod token;
mod traits;
mod treasury;
mod types;
mod upgrades;
mod vesting;
mod govtoken; // if I put this in tests/ , I seem unable to use declare('MyToken')
mod voting_token;
mod testing {
mod setup;
}
87 changes: 87 additions & 0 deletions tests/airdrop_tests.cairo
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
// }


2 changes: 1 addition & 1 deletion tests/basic.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn test_submit_proposal() {
let dispatcher = IProposalsDispatcher { contract_address: gov_contract_addr };
// corresponding govtoken: 0x05151bfdd47826df3669033ea7fb977d3b2d45c4f4d1c439a9edf4062bf34bfa
// has one holder, with 31 CARM: 0x0583a9d956d65628f806386ab5b12dccd74236a3c6b930ded9cf3c54efc722a1
let admin_addr: ContractAddress =
let _admin_addr: ContractAddress =
0x0583a9d956d65628f806386ab5b12dccd74236a3c6b930ded9cf3c54efc722a1
.try_into()
.unwrap();
Expand Down
4 changes: 3 additions & 1 deletion tests/lib.cairo
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;
164 changes: 164 additions & 0 deletions tests/proposals_tests.cairo
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);
}
83 changes: 83 additions & 0 deletions tests/setup.cairo
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
}

0 comments on commit 802bbef

Please sign in to comment.