Skip to content

Commit

Permalink
Adding Burner and setting Status for Carbon Vintage (#47)
Browse files Browse the repository at this point in the history
* impl test for minter and add mock usdcarb module for testing

* add ERC1155 impl and add minter tests

* Create component BurnHandler and create contract Burner

* set vintage status and adding tests cases

* refactor: Remove unused functions from project contract
  • Loading branch information
Arn0d authored May 6, 2024
1 parent 3b65893 commit 3f31892
Show file tree
Hide file tree
Showing 17 changed files with 771 additions and 94 deletions.
48 changes: 45 additions & 3 deletions src/components/absorber/carbon_handler.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ mod AbsorberComponent {
use alexandria_storage::list::{List, ListTrait};

// Internal imports
use carbon_v3::components::absorber::interface::IAbsorber;
use carbon_v3::components::absorber::interface::ICarbonCreditsHandler;
use carbon_v3::components::data::carbon_vintage::{CarbonVintage, CarbonVintageType};
use carbon_v3::components::absorber::interface::{IAbsorber, ICarbonCreditsHandler};
use carbon_v3::data::carbon_vintage::{CarbonVintage, CarbonVintageType};

// Constants

Expand Down Expand Up @@ -51,6 +50,7 @@ mod AbsorberComponent {
value: u256
}


mod Errors {
const INVALID_ARRAY_LENGTH: felt252 = 'Absorber: invalid array length';
const INVALID_STARTING_YEAR: felt252 = 'Absorber: invalid starting year';
Expand Down Expand Up @@ -281,6 +281,36 @@ mod AbsorberComponent {
fn get_cc_decimals(self: @ComponentState<TContractState>) -> u8 {
CC_DECIMALS
}

fn update_vintage_status(
ref self: ComponentState<TContractState>, year: u64, status: felt252
) {
let mut carbon_vintages: List<CarbonVintage> = self.Absorber_vintage_cc.read();
let mut index = 0;

loop {
if index == carbon_vintages.len() {
break ();
}

let mut tmp_vintage: CarbonVintage = self.Absorber_vintage_cc.read()[index];
if tmp_vintage.cc_vintage == year.into() {
let new_status: CarbonVintageType = match status {
0 => CarbonVintageType::Projected,
1 => CarbonVintageType::Confirmed,
2 => CarbonVintageType::Audited,
3 => CarbonVintageType::Retired,
_ => CarbonVintageType::Projected,
};

tmp_vintage.cc_status = new_status;
let _ = carbon_vintages.set(index, tmp_vintage);
}
index += 1;
};

self.Absorber_vintage_cc.write(carbon_vintages);
}
}

#[generate_trait]
Expand Down Expand Up @@ -364,6 +394,18 @@ mod AbsorberComponent {
};
array.span()
}

fn __felt252_into_CarbonVintageType(
self: @ComponentState<TContractState>, status: felt252
) -> CarbonVintageType {
match status {
0 => CarbonVintageType::Projected,
1 => CarbonVintageType::Confirmed,
2 => CarbonVintageType::Audited,
3 => CarbonVintageType::Retired,
_ => CarbonVintageType::Projected,
}
}
}
}

5 changes: 4 additions & 1 deletion src/components/absorber/interface.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use starknet::ContractAddress;
use carbon_v3::components::data::carbon_vintage::{CarbonVintage};
use carbon_v3::data::carbon_vintage::{CarbonVintage};

#[starknet::interface]
trait IAbsorber<TContractState> {
Expand Down Expand Up @@ -54,4 +54,7 @@ trait ICarbonCreditsHandler<TContractState> {

// Get number of decimal for total supply to have a carbon credit
fn get_cc_decimals(self: @TContractState) -> u8;

// Update the vintage status
fn update_vintage_status(ref self: TContractState, year: u64, status: felt252);
}
4 changes: 4 additions & 0 deletions src/components/burner.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod burn_handler;
mod interface;

use burn_handler::BurnComponent;
233 changes: 233 additions & 0 deletions src/components/burner/burn_handler.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
#[starknet::component]
mod BurnComponent {
// Core imports

use core::clone::Clone;
use core::array::SpanTrait;
use zeroable::Zeroable;
use traits::{Into, TryInto};
use option::OptionTrait;
use array::{Array, ArrayTrait};
use hash::HashStateTrait;
use poseidon::PoseidonTrait;

// Starknet imports

use starknet::ContractAddress;
use starknet::{get_caller_address, get_contract_address, get_block_timestamp};

// Internal imports

use carbon_v3::components::burner::interface::IBurnHandler;
use carbon_v3::data::carbon_vintage::{CarbonVintage, CarbonVintageType};
use carbon_v3::components::absorber::interface::{IAbsorberDispatcher, IAbsorberDispatcherTrait};
use carbon_v3::components::absorber::interface::{
ICarbonCreditsHandlerDispatcher, ICarbonCreditsHandlerDispatcherTrait
};
use carbon_v3::components::erc1155::interface::{IERC1155Dispatcher, IERC1155DispatcherTrait};
use carbon_v3::contracts::project::{
IExternalDispatcher as IProjectDispatcher,
IExternalDispatcherTrait as IProjectDispatcherTrait
};

// Constants

const MULT_ACCURATE_SHARE: u256 = 1_000_000;

#[storage]
struct Storage {
Burn_carbonable_project_address: ContractAddress,
Burn_carbon_pending_retirement: LegacyMap<(u256, ContractAddress), u256>,
Burn_carbon_retired: LegacyMap<(u256, ContractAddress), u256>,
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
RequestedRetirement: RequestedRetirement,
Retired: Retired,
}

#[derive(Drop, starknet::Event)]
struct RequestedRetirement {
#[key]
from: ContractAddress,
#[key]
project: ContractAddress,
#[key]
vintage: u256,
amount: u256,
}

#[derive(Drop, starknet::Event)]
struct Retired {
#[key]
from: ContractAddress,
#[key]
project: ContractAddress,
#[key]
vintage: u256,
amount: u256,
}

mod Errors {
const INVALID_VINTAGE_STATUS: felt252 = 'vintage status is not audited';
}

#[embeddable_as(BurnHandlerImpl)]
impl BurnHandler<
TContractState, +HasComponent<TContractState>, +Drop<TContractState>
> of IBurnHandler<ComponentState<TContractState>> {
fn retire_carbon_credits(
ref self: ComponentState<TContractState>, vintage: u256, carbon_values: u256
) {
// [Setup] Setup variable and contract interaction
let caller_address: ContractAddress = get_caller_address();
let project_address: ContractAddress = self.Burn_carbonable_project_address.read();

// [Check] Vintage have the right status
let carbon_credits = ICarbonCreditsHandlerDispatcher {
contract_address: project_address
};
let stored_vintage: CarbonVintage = carbon_credits
.get_specific_carbon_vintage(vintage.try_into().expect('Invalid vintage year'));
assert(
stored_vintage.cc_status == CarbonVintageType::Audited,
'Vintage status is not audited'
);

// [Check] caller owns the carbon credits for the vintage
let erc1155 = IERC1155Dispatcher { contract_address: project_address };
let caller_balance = erc1155.balance_of(caller_address, vintage);
assert(caller_balance >= carbon_values, 'Not own enough carbon credits');

// [Effect] Add pending retirement
self._add_pending_retirement(caller_address, vintage, carbon_values);

// [Effect] Burn carbon credits
self._burn_carbon_credit(caller_address, vintage, carbon_values);
}

fn retire_list_carbon_credits(
ref self: ComponentState<TContractState>,
vintages: Span<u256>,
carbon_values: Span<u256>
) {
// [Check] vintages and carbon values are defined
assert(vintages.len() > 0, 'Inputs cannot be empty');
assert(vintages.len() == carbon_values.len(), 'Vintages and Values mismatch');

let mut index: u32 = 0;
loop {
// [Check] Vintage is defined
let vintage = match vintages.get(index) {
Option::Some(value) => *value.unbox(),
Option::None => 0,
};
let carbon_amount = match carbon_values.get(index) {
Option::Some(value) => *value.unbox(),
Option::None => 0,
};

if vintage != 0 && carbon_amount != 0 {
self.retire_carbon_credits(vintage, carbon_amount);
}

index += 1;
if index == vintages.len() {
break;
}
};
}

fn get_pending_retirement(ref self: ComponentState<TContractState>, vintage: u256) -> u256 {
let caller_address: ContractAddress = get_caller_address();
self.Burn_carbon_pending_retirement.read((vintage, caller_address))
}

fn get_carbon_retired(ref self: ComponentState<TContractState>, vintage: u256) -> u256 {
let caller_address: ContractAddress = get_caller_address();
self.Burn_carbon_retired.read((vintage, caller_address))
}
}

#[generate_trait]
impl InternalImpl<
TContractState, +HasComponent<TContractState>, +Drop<TContractState>
> of InternalTrait<TContractState> {
fn initializer(
ref self: ComponentState<TContractState>, carbonable_project_address: ContractAddress
) {
// [Effect] Update storage
self.Burn_carbonable_project_address.write(carbonable_project_address);
}

fn _add_pending_retirement(
ref self: ComponentState<TContractState>,
from: ContractAddress,
vintage: u256,
amount: u256
) {
let current_pending_retirement = self
.Burn_carbon_pending_retirement
.read((vintage, from));
let new_pending_retirement = current_pending_retirement + amount;
self.Burn_carbon_pending_retirement.write((vintage, from), new_pending_retirement);

// [Event] Emit event
self
.emit(
RequestedRetirement {
from: from,
project: self.Burn_carbonable_project_address.read(),
vintage: vintage,
amount: amount
}
);
}

fn _remove_pending_retirement(
ref self: ComponentState<TContractState>,
from: ContractAddress,
vintage: u256,
amount: u256
) {
let current_pending_retirement = self
.Burn_carbon_pending_retirement
.read((vintage, from));
assert(current_pending_retirement >= amount, 'Not enough pending retirement');
let new_pending_retirement = current_pending_retirement - amount;
self.Burn_carbon_pending_retirement.write((vintage, from), new_pending_retirement);
}

fn _burn_carbon_credit(
ref self: ComponentState<TContractState>,
from: ContractAddress,
vintage: u256,
amount: u256
) {
// [Effect] Remove pending retirement
self._remove_pending_retirement(from, vintage, amount);

// [Effect] Update storage
let project = IProjectDispatcher {
contract_address: self.Burn_carbonable_project_address.read()
};
project.burn(from, vintage, amount);
let current_retirement = self.Burn_carbon_retired.read((vintage, from));
let new_retirement = current_retirement + amount;
self.Burn_carbon_retired.write((vintage, from), new_retirement);

// [Event] Emit event
self
.emit(
Retired {
from: from,
project: self.Burn_carbonable_project_address.read(),
vintage: vintage,
amount: amount
}
);
}
}
}
22 changes: 22 additions & 0 deletions src/components/burner/interface.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use starknet::ContractAddress;
use carbon_v3::data::carbon_vintage::{CarbonVintage};

#[starknet::interface]
trait IBurnHandler<TContractState> {
/// Retire carbon credits from one vintage of carbon credits.
fn retire_carbon_credits(ref self: TContractState, vintage: u256, carbon_values: u256);

/// 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,
/// 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>
);

/// Get the pending retirement of a vintage for the caller address.
fn get_pending_retirement(ref self: TContractState, vintage: u256) -> u256;

/// Get the carbon retirement of a vintage for the caller address.
fn get_carbon_retired(ref self: TContractState, vintage: u256) -> u256;
}
2 changes: 0 additions & 2 deletions src/components/data.cairo

This file was deleted.

21 changes: 0 additions & 21 deletions src/components/data/carbon_project.cairo

This file was deleted.

3 changes: 1 addition & 2 deletions src/components/minter/mint.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ mod MintComponent {
IExternalDispatcher as IProjectDispatcher,
IExternalDispatcherTrait as IProjectDispatcherTrait
};
use carbon_v3::components::data::carbon_vintage::{CarbonVintage, CarbonVintageType};
use carbon_v3::data::carbon_vintage::{CarbonVintage, CarbonVintageType};

// Constants

Expand Down Expand Up @@ -269,7 +269,6 @@ mod MintComponent {
self.Mint_remaining_money_amount.write(remaining_money_amount - money_amount);

// [Interaction] Mint
// Implement Span<u256> to return the list of cc_vintage (token_id & year)
let project = IProjectDispatcher { contract_address: project_address };
project.batch_mint(caller_address, cc_years_vintages, cc_distribution);

Expand Down
Loading

0 comments on commit 3f31892

Please sign in to comment.