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

Feat/monthly mint with minted balance and received balance #16

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions contracts/src/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ pub trait IKudos<TState> {
description: felt252,
);
fn register_sw_employee(ref self: TState, credential_hash: felt252,);
fn monthly_mint(ref self: TState);
fn get_total_given(self: @TState, address: ContractAddress) -> u256;
fn get_total_received(self: @TState, address: ContractAddress) -> u256;
fn get_minted_balance(self: @TState, address: ContractAddress) -> u256;
fn get_last_mint_timestamp(self: @TState, address: ContractAddress) -> u64;
}
48 changes: 45 additions & 3 deletions contracts/src/kudos.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ pub mod Kudos {
use kudos::credential_registry::{ICredentialRegistry, CredentialRegistryComponent};
use kudos::oz16::IERC20ReadOnly;
use kudos::oz16::erc20::{ERC20Component, ERC20HooksEmptyImpl, ERC20Component::InternalTrait};
use kudos::utils::constants::{REGISTRATION_AMOUNT, ONE};
use kudos::utils::constants::{
REGISTRATION_AMOUNT, ONE, SECONDS_IN_30_DAYS, MONTHLT_MINT_AMOUNT
};
use starknet::storage::{
StoragePointerReadAccess, StoragePointerWriteAccess, StoragePathEntry, Map
};
use starknet::{ContractAddress, get_caller_address};
use starknet::{ContractAddress, get_caller_address, get_block_timestamp};

component!(
path: CredentialRegistryComponent,
Expand All @@ -31,7 +33,9 @@ pub mod Kudos {
#[substorage(v0)]
erc20: ERC20Component::Storage,
total_given: Map<ContractAddress, u256>,
total_received: Map<ContractAddress, u256>
total_received: Map<ContractAddress, u256>,
minted_balance: Map<ContractAddress, u256>,
last_mint_timestamp: Map<ContractAddress, u64>,
}

#[event]
Expand All @@ -56,6 +60,11 @@ pub mod Kudos {
pub mod Errors {
pub const SENDER_UNREGISTERED: felt252 = 'Sender not registered';
pub const RECEIVER_UNREGISTERED: felt252 = 'Receiver not registered';
pub const MINTER_UNREGISTERED: felt252 = 'Minter not registered';
pub const MINTED_BALANCE_ZERO: felt252 = 'Minted balance is zero';
pub const MINTED_BALANCE_IS_FULL: felt252 = 'Minted balance is full';
pub const MINTED_AMOUNT_IS_ZERO: felt252 = 'Minted amount is zero';
pub const MINTED_LESS_THAN_30_DAYS_AGO: felt252 = 'Minted less than 30 days ago';
}

#[constructor]
Expand All @@ -77,6 +86,9 @@ pub mod Kudos {
let receiver = self.credential_registry.get_credential_address(receiver_credentials);
assert(self.credential_registry.is_registered(receiver), Errors::RECEIVER_UNREGISTERED);

let minted_balance = self.minted_balance.entry(sender).read();
assert(minted_balance > 0, Errors::MINTED_BALANCE_ZERO);

self.erc20.transfer(receiver, ONE);

let total_given = self.total_given.entry(sender).read();
Expand All @@ -85,13 +97,35 @@ pub mod Kudos {
let total_received = self.total_given.entry(receiver).read();
self.total_received.entry(receiver).write(total_received + ONE);

self.minted_balance.entry(sender).write(minted_balance - ONE);

self.emit(KudosGiven { sender, receiver, description });
}

fn monthly_mint(ref self: ContractState) {
let address = get_caller_address();
assert(self.credential_registry.is_registered(address), Errors::MINTER_UNREGISTERED);

let last_mint_timestamp = self.last_mint_timestamp.entry(address).read();
let current_timestamp = get_block_timestamp();
let time_since_last_mint = current_timestamp - last_mint_timestamp;
assert(time_since_last_mint > SECONDS_IN_30_DAYS, Errors::MINTED_LESS_THAN_30_DAYS_AGO);

let amount_to_mint = MONTHLT_MINT_AMOUNT - self.minted_balance.entry(address).read();
assert(amount_to_mint > 0, Errors::MINTED_BALANCE_IS_FULL);

self.erc20.mint(address, amount_to_mint);
self.minted_balance.entry(address).write(MONTHLT_MINT_AMOUNT);
self.last_mint_timestamp.entry(address).write(current_timestamp);
}

fn register_sw_employee(ref self: ContractState, credential_hash: felt252,) {
let caller = get_caller_address();
self.register_credentials(caller, credential_hash);
let current_timestamp = get_block_timestamp();
self.erc20.mint(caller, REGISTRATION_AMOUNT);
self.minted_balance.entry(caller).write(REGISTRATION_AMOUNT);
self.last_mint_timestamp.entry(caller).write(current_timestamp);
}

fn get_total_given(self: @ContractState, address: ContractAddress) -> u256 {
Expand All @@ -101,6 +135,14 @@ pub mod Kudos {
fn get_total_received(self: @ContractState, address: ContractAddress) -> u256 {
self.total_received.entry(address).read()
}

fn get_minted_balance(self: @ContractState, address: ContractAddress) -> u256 {
self.minted_balance.entry(address).read()
}

fn get_last_mint_timestamp(self: @ContractState, address: ContractAddress) -> u64 {
self.last_mint_timestamp.entry(address).read()
}
}

#[abi(embed_v0)]
Expand Down
6 changes: 5 additions & 1 deletion contracts/src/utils/constants.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ pub fn ZERO_ADDRESS() -> ContractAddress {
//

pub const DECIMALS: u8 = 18;
pub const ZERO: u256 = 0;
pub const ONE: u256 = 1_000_000_000_000_000_000;
pub const REGISTRATION_AMOUNT: u256 = 5_000_000_000_000_000_000;
pub const FIVE: u256 = 5_000_000_000_000_000_000;
pub const REGISTRATION_AMOUNT: u256 = FIVE;
pub const MONTHLT_MINT_AMOUNT: u256 = FIVE;
pub const SECONDS_IN_30_DAYS: u64 = 30 * 24 * 60 * 60;

pub fn NAME() -> ByteArray {
"Kudos"
Expand Down
74 changes: 71 additions & 3 deletions contracts/tests/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ use kudos::oz16::erc20::ERC20Component;
use kudos::oz16::{IERC20Dispatcher, IERC20DispatcherTrait};
use kudos::utils::constants::{
CALLER, NAME, SYMBOL, DECIMALS, CREDENTIAL_HASH, CREDENTIAL_HASH_2, REGISTRATION_AMOUNT,
ZERO_ADDRESS, RECEIVER, DUMMY, CREDENTIAL_HASH_BAD
ZERO_ADDRESS, RECEIVER, DUMMY, CREDENTIAL_HASH_BAD, SECONDS_IN_30_DAYS, FIVE, ZERO
};
use kudos::{Kudos, IKudosDispatcher, IKudosDispatcherTrait};
use snforge_std::{spy_events, EventSpyAssertionsTrait, start_cheat_caller_address};
use utils::{setup, setup_registered, test_description, one};
use snforge_std::{
spy_events, EventSpyAssertionsTrait, start_cheat_caller_address,
start_cheat_block_timestamp_global, stop_cheat_block_timestamp_global
};
use starknet::get_block_timestamp;
use utils::{setup, setup_registered, test_description, one, send_5_kudos};

#[test]
fn test_erc20_metadata() {
Expand Down Expand Up @@ -113,3 +117,67 @@ fn test_give_kudos_receiver_unregistered() {
start_cheat_caller_address(kudos.contract_address, CALLER());
kudos.give_kudos(CREDENTIAL_HASH, CREDENTIAL_HASH_BAD, test_description());
}

#[test]
#[should_panic(expected: 'Minted balance is zero')]
fn test_give_kudos_minted_balance_zero() {
let kudos = IKudosDispatcher { contract_address: setup_registered() };
start_cheat_caller_address(kudos.contract_address, CALLER());
send_5_kudos(kudos);

kudos.give_kudos(CREDENTIAL_HASH, CREDENTIAL_HASH_2, test_description());
}

#[test]
fn test_monthly_mint_full_amount() {
let kudos = IKudosDispatcher { contract_address: setup_registered() };

start_cheat_caller_address(kudos.contract_address, CALLER());
send_5_kudos(kudos);

let thirty_days_pass = get_block_timestamp() + SECONDS_IN_30_DAYS + 1;
start_cheat_block_timestamp_global(block_timestamp: thirty_days_pass);
assert(kudos.get_minted_balance(CALLER()) == ZERO, 'Minted balance is not zero');

kudos.monthly_mint();

assert(kudos.get_minted_balance(CALLER()) == FIVE, 'Minted balance is not five');

stop_cheat_block_timestamp_global()
}

#[test]
#[should_panic(expected: 'Minted balance is full')]
fn test_monthly_mint_with_a_full_balance() {
let kudos = IKudosDispatcher { contract_address: setup_registered() };

start_cheat_caller_address(kudos.contract_address, CALLER());

let thirty_days_pass = get_block_timestamp() + SECONDS_IN_30_DAYS + 1;
start_cheat_block_timestamp_global(block_timestamp: thirty_days_pass);
assert(kudos.get_minted_balance(CALLER()) == FIVE, 'Minted balance is not five');

kudos.monthly_mint();

stop_cheat_block_timestamp_global()
}

#[test]
#[should_panic(expected: 'Minter not registered')]
fn test_monthly_mint_minter_unregistered() {
let kudos = IKudosDispatcher { contract_address: setup_registered() };
start_cheat_caller_address(kudos.contract_address, DUMMY());
kudos.monthly_mint();
}

#[test]
fn test_get_last_mint_timestamp() {
start_cheat_block_timestamp_global(block_timestamp: 42);
let kudos = IKudosDispatcher { contract_address: setup_registered() };
start_cheat_caller_address(kudos.contract_address, CALLER());

assert(kudos.get_last_mint_timestamp(CALLER()) == 42, 'Last mint time is incorrect');

stop_cheat_block_timestamp_global();
}

8 changes: 8 additions & 0 deletions contracts/tests/utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ pub fn setup_registered() -> ContractAddress {
contract_address
}

pub fn send_5_kudos(dispatcher: IKudosDispatcher) {
let mut counter: u32 = 0;
while counter < 5 {
dispatcher.give_kudos(CREDENTIAL_HASH, CREDENTIAL_HASH_2, test_description());
counter += 1;
}
}

pub fn one() -> u256 {
ONE * 1
}
Expand Down
Loading