Skip to content

Commit

Permalink
Merge pull request #122 from julienbrs/add-remaining-events
Browse files Browse the repository at this point in the history
Add remaining events + test events
  • Loading branch information
julienbrs authored Jul 26, 2024
2 parents 2f264a7 + 9bb8233 commit 86a55ce
Show file tree
Hide file tree
Showing 11 changed files with 634 additions and 449 deletions.
2 changes: 1 addition & 1 deletion src/components/minter/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ trait IMint<TContractState> {
recipient: ContractAddress,
amount: u256
);
fn public_buy(ref self: TContractState, money_amount: u256, force: bool) -> Span<u256>;
fn public_buy(ref self: TContractState, money_amount: u256);
fn cancel_mint(ref self: TContractState, should_cancel: bool);
}
170 changes: 88 additions & 82 deletions src/components/minter/mint.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -46,35 +46,65 @@ mod MintComponent {
SoldOut: SoldOut,
Buy: Buy,
MintCanceled: MintCanceled,
UnitPriceUpdated: UnitPriceUpdated,
Withdraw: Withdraw,
AmountRetrieved: AmountRetrieved,
MinMoneyAmountPerTxUpdated: MinMoneyAmountPerTxUpdated,
}

#[derive(Drop, starknet::Event)]
struct PublicSaleOpen {
time: u64
old_value: bool,
new_value: bool
}

#[derive(Drop, starknet::Event)]
struct PublicSaleClose {
time: u64
old_value: bool,
new_value: bool
}

#[derive(Drop, starknet::Event)]
struct SoldOut {
time: u64
}
struct SoldOut {}

#[derive(Drop, starknet::Event)]
struct Buy {
#[key]
address: ContractAddress,
money_amount: u256,
vintages: Span<u256>,
shares: u256,
}

#[derive(Drop, starknet::Event)]
struct MintCanceled {
is_canceled: bool,
time: u64
old_value: bool,
is_canceled: bool
}

#[derive(Drop, starknet::Event)]
struct UnitPriceUpdated {
old_price: u256,
new_price: u256,
}

#[derive(Drop, starknet::Event)]
struct Withdraw {
recipient: ContractAddress,
amount: u256,
}


#[derive(Drop, starknet::Event)]
struct AmountRetrieved {
token_address: ContractAddress,
recipient: ContractAddress,
amount: u256,
}

#[derive(Drop, starknet::Event)]
struct MinMoneyAmountPerTxUpdated {
old_amount: u256,
new_amount: u256,
}

mod Errors {
Expand Down Expand Up @@ -118,21 +148,16 @@ mod MintComponent {
fn cancel_mint(ref self: ComponentState<TContractState>, should_cancel: bool) {
let project_address = self.Mint_carbonable_project_address.read();
let project = IProjectDispatcher { contract_address: project_address };
// [Check] Caller is not zero

let caller_address = get_caller_address();
assert(!caller_address.is_zero(), 'Invalid caller');
// [Check] Caller is owner
let isOwner = project.only_owner(caller_address);
assert(isOwner, 'Caller is not the owner');

// [Effect] Cancel the mint
let old_value: bool = self.Mint_cancel.read();
self.Mint_cancel.write(should_cancel);

// Get the current timestamp
let current_time = get_block_timestamp();

// [Event] Emit cancel event
self.emit(MintCanceled { is_canceled: should_cancel, time: current_time });
self.emit(MintCanceled { old_value: old_value, is_canceled: should_cancel });
}

fn is_canceled(self: @ComponentState<TContractState>) -> bool {
Expand All @@ -142,61 +167,52 @@ mod MintComponent {
fn set_public_sale_open(ref self: ComponentState<TContractState>, public_sale_open: bool) {
let project_address = self.Mint_carbonable_project_address.read();
let project = IProjectDispatcher { contract_address: project_address };
// [Check] Caller is not zero

let caller_address = get_caller_address();
assert(!caller_address.is_zero(), 'Invalid caller');
// [Check] Caller is owner
let isOwner = project.only_owner(caller_address);
assert(isOwner, 'Caller is not the owner');

// [Effect] Update storage
let old_value = self.Mint_public_sale_open.read();
self.Mint_public_sale_open.write(public_sale_open);

// [Event] Emit event
let current_time = get_block_timestamp();
if public_sale_open {
self.emit(PublicSaleOpen { time: current_time });
} else {
self.emit(PublicSaleClose { time: current_time });
};
self.emit(PublicSaleOpen { old_value: old_value, new_value: public_sale_open });
}

fn set_unit_price(ref self: ComponentState<TContractState>, unit_price: u256) {
let project_address = self.Mint_carbonable_project_address.read();
let project = IProjectDispatcher { contract_address: project_address };

// [Check] Caller is not zero
let caller_address = get_caller_address();
assert(!caller_address.is_zero(), 'Invalid caller');

// [Check] Caller is owner
let isOwner = project.only_owner(caller_address);
assert(isOwner, 'Caller is not the owner');

// [Check] Value not null
assert(unit_price > 0, 'Invalid unit price');
// [Effect] Store value

let old_price = self.Mint_unit_price.read();
self.Mint_unit_price.write(unit_price);

self.emit(UnitPriceUpdated { old_price: old_price, new_price: unit_price });
}

fn withdraw(ref self: ComponentState<TContractState>) {
let project_address = self.Mint_carbonable_project_address.read();
let project = IProjectDispatcher { contract_address: project_address };
// [Check] Caller is not zero

let caller_address = get_caller_address();
assert(!caller_address.is_zero(), 'Invalid caller');
// [Check] Caller is owner
let isOwner = project.only_owner(caller_address);
assert(isOwner, 'Caller is not the owner');
// [Compute] Balance to withdraw

let token_address = self.Mint_payment_token_address.read();
let erc20 = IERC20Dispatcher { contract_address: token_address };
let contract_address = get_contract_address();
let balance = erc20.balance_of(contract_address);

// [Interaction] Transfer tokens
let success = erc20.transfer(caller_address, balance);
assert(success, 'Transfer failed');

self.emit(Withdraw { recipient: caller_address, amount: balance });
}

fn retrieve_amount(
Expand All @@ -207,25 +223,29 @@ mod MintComponent {
) {
let project_address = self.Mint_carbonable_project_address.read();
let project = IProjectDispatcher { contract_address: project_address };

let caller_address = get_caller_address();
assert(!caller_address.is_zero(), 'Invalid caller');
// [Check] Caller is owner
let isOwner = project.only_owner(caller_address);
assert(isOwner, 'Caller is not the owner');

let erc20 = IERC20Dispatcher { contract_address: token_address };
let success = erc20.transfer(recipient, amount);
assert(success, 'Transfer failed');

self
.emit(
AmountRetrieved {
token_address: token_address, recipient: recipient, amount: amount
}
);
}

fn public_buy(
ref self: ComponentState<TContractState>, money_amount: u256, force: bool
) -> Span<u256> {
// [Check] Public sale is open
fn public_buy(ref self: ComponentState<TContractState>, money_amount: u256) {
let public_sale_open = self.Mint_public_sale_open.read();
assert(public_sale_open, 'Sale is closed');
// [Interaction] Buy
self._buy(money_amount, force)

self._buy(money_amount);
}

fn set_min_money_amount_per_tx(
Expand All @@ -234,22 +254,25 @@ mod MintComponent {
let project_address = self.Mint_carbonable_project_address.read();
let project = IProjectDispatcher { contract_address: project_address };

// [Check] Caller is not zero
let caller_address = get_caller_address();
assert(!caller_address.is_zero(), 'Invalid caller');

// [Check] Caller is owner
let isOwner = project.only_owner(caller_address);
assert(isOwner, 'Caller is not the owner');

// [Check] Value in range
let max_money_amount_per_tx = self.Mint_max_money_amount.read();
let max_money_amount_per_tx = self.get_max_money_amount();
assert(
max_money_amount_per_tx >= min_money_amount_per_tx,
'Invalid min money amount per tx'
);
// [Effect] Store value

let old_amount = self.Mint_min_money_amount_per_tx.read();
self.Mint_min_money_amount_per_tx.write(min_money_amount_per_tx);
self
.emit(
MinMoneyAmountPerTxUpdated {
old_amount: old_amount, new_amount: min_money_amount_per_tx,
}
);
}

fn get_max_money_amount(self: @ComponentState<TContractState>) -> u256 {
Expand All @@ -269,50 +292,36 @@ mod MintComponent {
max_money_amount: u256,
unit_price: u256,
) {
// [Check] Input consistency
assert(unit_price > 0, 'Invalid unit price');

// [Effect] Update storage
self.Mint_carbonable_project_address.write(carbonable_project_address);

// [Effect] Update storage
self.Mint_payment_token_address.write(payment_token_address);
self.Mint_unit_price.write(unit_price);
self.Mint_remaining_money_amount.write(max_money_amount);
self.Mint_max_money_amount.write(max_money_amount);

// [Effect] Use dedicated function to emit corresponding events
self.Mint_public_sale_open.write(public_sale_open);
}

fn _buy(
ref self: ComponentState<TContractState>, money_amount: u256, force: bool
) -> Span<u256> {
// [Check] Value not null
fn _buy(ref self: ComponentState<TContractState>, money_amount: u256) {
assert(money_amount > 0, 'Invalid amount of money');

// [Check] Caller is not zero
let caller_address = get_caller_address();
assert(!caller_address.is_zero(), 'Invalid caller');

// [Check] Allowed value
let min_money_amount_per_tx = self.Mint_min_money_amount_per_tx.read();
assert(money_amount >= min_money_amount_per_tx, 'Value too low');

// [Check] Allowed enough remaining_money
let remaining_money_amount = self.Mint_remaining_money_amount.read();

assert(money_amount <= remaining_money_amount, 'Not enough remaining money');
// [Interaction] Compute share of the amount of project
let max_money_amount = self.Mint_max_money_amount.read();

let max_money_amount = self.get_max_money_amount();
let share = money_amount * CC_DECIMALS_MULTIPLIER / max_money_amount;

// [Interaction] Compute the amount of cc for each vintage
let project_address = self.Mint_carbonable_project_address.read();
let vintages = IVintageDispatcher { contract_address: project_address };
let num_vintages: usize = vintages.get_num_vintages();

// Initially, share is the same for all the vintages
// User mints the same amount of tokens for all the vintages
let mut cc_shares: Array<u256> = Default::default();
let mut tokens: Array<u256> = Default::default();
let mut index = 0;
Expand All @@ -327,40 +336,37 @@ mod MintComponent {
let cc_shares = cc_shares.span();
let token_ids = tokens.span();

// [Interaction] Pay
let token_address = self.Mint_payment_token_address.read();
let erc20 = IERC20Dispatcher { contract_address: token_address };
let minter_address = get_contract_address();

let success = erc20.transfer_from(caller_address, minter_address, money_amount);
// [Check] Transfer successful
assert(success, 'Transfer failed');

// [Interaction] Update remaining money amount
self.Mint_remaining_money_amount.write(remaining_money_amount - money_amount);

// [Interaction] Mint
let project = IProjectDispatcher { contract_address: project_address };
project.batch_mint(caller_address, token_ids, cc_shares);

// [Event] Emit event
let current_time = get_block_timestamp();
self
.emit(
Event::Buy(Buy { address: caller_address, vintages: token_ids, shares: share })
Event::Buy(
Buy {
address: caller_address, money_amount: money_amount, vintages: token_ids
}
)
);

// [Effect] Close the sale if sold out
if self.is_sold_out() {
// [Effect] Close public sale
self.Mint_public_sale_open.write(false);

// [Event] Emit sold out event
self.emit(Event::SoldOut(SoldOut { time: current_time }));
self
.emit(
Event::PublicSaleClose(
PublicSaleClose { old_value: true, new_value: false }
)
);
self.emit(Event::SoldOut(SoldOut {}));
};

// [Return] cc shares
cc_shares
}
}
}
2 changes: 1 addition & 1 deletion src/components/offsetter/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ trait IOffsetHandler<TContractState> {

/// Retire carbon credits from the list of carbon credits.
/// Behaviour is :
/// - If one of the carbon values is not enough or vintage status is not righ,
/// - If one of the carbon values is not enough or vintage status is not right,
/// the function will fail and no carbon will be retired and the function will revert.
fn retire_list_carbon_credits(
ref self: TContractState, vintages: Span<u256>, carbon_values: Span<u256>
Expand Down
Loading

0 comments on commit 86a55ce

Please sign in to comment.