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

add-carbon-offset-to-claim-and-tests #153

Merged
merged 5 commits into from
Nov 12, 2024
Merged
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
18 changes: 17 additions & 1 deletion src/components/offsetter/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,26 @@ trait IOffsetHandler<TContractState> {
ref self: TContractState, token_ids: Span<u256>, cc_amounts: Span<u256>
);

fn claim(
///Verify and validate the proof on the Merkle tree side to confirm the offset.
fn confirm_for_merkle_tree(
tekkac marked this conversation as resolved.
Show resolved Hide resolved
ref self: TContractState,
from: ContractAddress,
amount: u128,
timestamp: u128,
id: u128,
proof: Array::<felt252>
) -> bool;

///Verify on the business logic side, confirm on the Merkle tree side, and perform the offset action.
fn confirm_offset(
ref self: TContractState, amount: u128, timestamp: u128, id: u128, proof: Array::<felt252>
);

fn get_allocation_id(self: @TContractState, from: ContractAddress) -> u256;


fn get_retirement(self: @TContractState, token_id: u256, from: ContractAddress) -> u256;

/// Get the pending retirement of a vintage for the caller address.
fn get_pending_retirement(
self: @TContractState, address: ContractAddress, token_id: u256
Expand Down
54 changes: 44 additions & 10 deletions src/components/offsetter/offset_handler.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -154,34 +154,57 @@ mod OffsetComponent {
};
}

fn claim(
fn confirm_for_merkle_tree(
ref self: ComponentState<TContractState>,
tekkac marked this conversation as resolved.
Show resolved Hide resolved
from: ContractAddress,
amount: u128,
timestamp: u128,
id: u128,
proof: Array::<felt252>
) {
) -> bool {
let mut merkle_tree: MerkleTree<Hasher> = MerkleTreeImpl::new();
let claimee = get_caller_address();

// [Verify not already claimed]
let claimed = self.check_claimed(claimee, timestamp, amount, id);
assert(!claimed, 'Already claimed');

// [Verify the proof]
let amount_felt: felt252 = amount.into();
let claimee_felt: felt252 = claimee.into();
let from_felt: felt252 = from.into();
let timestamp_felt: felt252 = timestamp.into();
let id_felt: felt252 = id.into();

let intermediate_hash = LegacyHash::hash(claimee_felt, amount_felt);
let intermediate_hash = LegacyHash::hash(from_felt, amount_felt);
let intermediate_hash = LegacyHash::hash(intermediate_hash, timestamp_felt);
let leaf = LegacyHash::hash(intermediate_hash, id_felt);

let root_computed = merkle_tree.compute_root(leaf, proof.span());

let stored_root = self.Offsetter_merkle_root.read();
assert(root_computed == stored_root, 'Invalid proof');
if root_computed != stored_root {
assert(root_computed == stored_root, 'Invalid proof');
return false;
}

return true;
}

fn confirm_offset(
ref self: ComponentState<TContractState>,
amount: u128,
timestamp: u128,
id: u128,
proof: Array::<felt252>
) {
let claimee = get_caller_address();

// [Verify not already claimed]
let claimed = self.check_claimed(claimee, timestamp, amount, id);
assert(!claimed, 'Already claimed');

// [Verify if the merkle tree claim is possible]
assert(
self.confirm_for_merkle_tree(claimee, amount, timestamp, id, proof), 'Invalid proof'
);

//If everything is correct, we offset the carbon credits
self._offset_carbon_credit(claimee, 1, amount.into());

// [Mark as claimed]
let allocation = Allocation {
Expand All @@ -198,6 +221,17 @@ mod OffsetComponent {
);
}

fn get_allocation_id(self: @ComponentState<TContractState>, from: ContractAddress) -> u256 {
self.Offsetter_allocation_id.read(from)
}

fn get_retirement(
self: @ComponentState<TContractState>, token_id: u256, from: ContractAddress
) -> u256 {
self.Offsetter_carbon_retired.read((token_id, from))
}


fn get_pending_retirement(
self: @ComponentState<TContractState>, address: ContractAddress, token_id: u256
) -> u256 {
Expand Down
99 changes: 13 additions & 86 deletions tests/test_merkle_tree.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -66,33 +66,7 @@ fn test_bob_claims_single_allocation() {
start_cheat_caller_address(offsetter_address, bob_address);
assert_eq!(contract.get_merkle_root(), root);

assert!(!contract.check_claimed(bob_address, timestamp, amount, id));

start_cheat_caller_address(offsetter_address, bob_address);
contract.claim(amount, timestamp, id, proof);

assert!(contract.check_claimed(bob_address, timestamp, amount, id));
}

#[test]
#[should_panic(expected: 'Already claimed')]
fn test_bob_claims_twice() {
/// Test that trying to claim the same allocation twice results in a panic.
let owner_address = contract_address_const::<'OWNER'>();
let (root, bob_address, amount, timestamp, id, proof) = get_bob_first_wave_allocation();
let project_address = default_setup_and_deploy();
let offsetter_address = deploy_offsetter(project_address);
let contract = IOffsetHandlerDispatcher { contract_address: offsetter_address };

start_cheat_caller_address(offsetter_address, owner_address);
contract.set_merkle_root(root);
assert!(!contract.check_claimed(bob_address, timestamp, amount, id));

start_cheat_caller_address(offsetter_address, bob_address);
contract.claim(amount, timestamp, id, proof.clone());
assert!(contract.check_claimed(bob_address, timestamp, amount, id));

contract.claim(amount, timestamp, id, proof);
assert!(contract.confirm_for_merkle_tree(bob_address, amount, timestamp, id, proof));
}

#[test]
Expand All @@ -109,8 +83,7 @@ fn test_claim_with_invalid_address() {
let invalid_address = contract_address_const::<'DUMMY'>();
assert!(!contract.check_claimed(invalid_address, timestamp, amount, id));

start_cheat_caller_address(offsetter_address, invalid_address);
contract.claim(amount, timestamp, id, proof);
assert!(!contract.confirm_for_merkle_tree(invalid_address, amount, timestamp, id, proof));
}

#[test]
Expand All @@ -127,8 +100,7 @@ fn test_claim_with_invalid_amount() {
let invalid_amount = 0;
assert!(!contract.check_claimed(bob_address, timestamp, invalid_amount, id));

start_cheat_caller_address(offsetter_address, bob_address);
contract.claim(invalid_amount, timestamp, id, proof);
assert!(!contract.confirm_for_merkle_tree(bob_address, invalid_amount, timestamp, id, proof));
}

#[test]
Expand All @@ -145,8 +117,7 @@ fn test_claim_with_invalid_timestamp() {
let invalid_timestamp = 0;
assert!(!contract.check_claimed(bob_address, invalid_timestamp, amount, id));

start_cheat_caller_address(offsetter_address, bob_address);
contract.claim(amount, invalid_timestamp, id, proof);
assert!(!contract.confirm_for_merkle_tree(bob_address, amount, invalid_timestamp, id, proof));
}

#[test]
Expand All @@ -163,32 +134,7 @@ fn test_claim_with_invalid_proof() {
let invalid_proof: Array<felt252> = array![0x123, 0x1];
assert!(!contract.check_claimed(bob_address, timestamp, amount, id));

start_cheat_caller_address(offsetter_address, bob_address);
contract.claim(amount, timestamp, id, invalid_proof);
}

#[test]
fn test_event_emission_on_claim() {
let (root, bob_address, amount, timestamp, id, proof) = get_bob_first_wave_allocation();
let owner_address = contract_address_const::<'OWNER'>();
let project_address = default_setup_and_deploy();
let offsetter_address = deploy_offsetter(project_address);
let contract = IOffsetHandlerDispatcher { contract_address: offsetter_address };

start_cheat_caller_address(offsetter_address, owner_address);
contract.set_merkle_root(root);
assert!(!contract.check_claimed(bob_address, timestamp, amount, id));

let mut spy = spy_events();
start_cheat_caller_address(offsetter_address, bob_address);
contract.claim(amount, timestamp, id, proof);

let expected_event = OffsetComponent::Event::AllocationClaimed(
OffsetComponent::AllocationClaimed { claimee: bob_address, amount, timestamp, id }
);
spy.assert_emitted(@array![(offsetter_address, expected_event)]);

assert!(contract.check_claimed(bob_address, timestamp, amount, id));
assert!(!contract.confirm_for_merkle_tree(bob_address, amount, timestamp, id, invalid_proof));
}

#[test]
Expand All @@ -208,14 +154,12 @@ fn test_claim_after_root_update() {
contract.set_merkle_root(new_root);
assert!(!contract.check_claimed(bob_address, timestamp, amount, id));

start_cheat_caller_address(offsetter_address, bob_address);
contract.claim(amount, timestamp, id, new_proof);
assert!(contract.check_claimed(bob_address, timestamp, amount, id));
assert!(contract.confirm_for_merkle_tree(bob_address, amount, timestamp, id, new_proof));
}

#[test]
fn test_alice_claims_in_second_wave() {
/// Test that Bob can claim his allocation from the first wave and Alice can claim her allocation from the second wave.
/// Test that Bob can confirm his allocation from the first wave and Alice can confirm her allocation from the second wave.
let (root, bob_address, amount, timestamp, id, proof) = get_bob_first_wave_allocation();
let owner_address = contract_address_const::<'OWNER'>();
let project_address = default_setup_and_deploy();
Expand All @@ -226,30 +170,20 @@ fn test_alice_claims_in_second_wave() {
contract.set_merkle_root(root);
assert!(!contract.check_claimed(bob_address, timestamp, amount, id));

let mut spy = spy_events();
start_cheat_caller_address(offsetter_address, bob_address);
contract.claim(amount, timestamp, id, proof);

let expected_event = OffsetComponent::Event::AllocationClaimed(
OffsetComponent::AllocationClaimed { claimee: bob_address, amount, timestamp, id }
);
spy.assert_emitted(@array![(offsetter_address, expected_event)]);
assert!(contract.check_claimed(bob_address, timestamp, amount, id));
assert!(contract.confirm_for_merkle_tree(bob_address, amount, timestamp, id, proof));

let (new_root, alice_address, amount, timestamp, id, proof) =
get_alice_second_wave_allocation();
start_cheat_caller_address(offsetter_address, owner_address);
contract.set_merkle_root(new_root);
assert!(!contract.check_claimed(alice_address, timestamp, amount, id));

start_cheat_caller_address(offsetter_address, alice_address);
contract.claim(amount, timestamp, id, proof);
assert!(contract.check_claimed(alice_address, timestamp, amount, id));
assert!(contract.confirm_for_merkle_tree(alice_address, amount, timestamp, id, proof));
}

#[test]
fn test_john_claims_multiple_allocations() {
/// Test that John can claim two of his three allocations from the first wave, and the remaining one from the second wave.
/// Test that John can confirm_for_merkle_tree two of his three allocations from the first wave, and the remaining one from the second wave.
let (
root,
new_root,
Expand Down Expand Up @@ -284,18 +218,11 @@ fn test_john_claims_multiple_allocations() {
assert!(!contract.check_claimed(john_address, timestamp2, amount2, id_2));
assert!(!contract.check_claimed(john_address, timestamp3, amount3, id_3));

start_cheat_caller_address(offsetter_address, john_address);
contract.claim(amount1, timestamp1, id_1, proof1);
contract.claim(amount2, timestamp2, id_2, proof2);
assert!(contract.check_claimed(john_address, timestamp1, amount1, id_1));
assert!(contract.check_claimed(john_address, timestamp2, amount2, id_2));
assert!(!contract.check_claimed(john_address, timestamp3, amount3, id_3));
assert!(contract.confirm_for_merkle_tree(john_address, amount1, timestamp1, id_1, proof1));
assert!(contract.confirm_for_merkle_tree(john_address, amount2, timestamp2, id_2, proof2));

start_cheat_caller_address(offsetter_address, owner_address);
contract.set_merkle_root(new_root);

start_cheat_caller_address(offsetter_address, john_address);
contract.claim(amount4, timestamp4, id_4, proof4);
assert!(contract.check_claimed(john_address, timestamp4, amount4, id_4));
assert!(!contract.check_claimed(john_address, timestamp3, amount3, id_3));
assert!(contract.confirm_for_merkle_tree(john_address, amount4, timestamp4, id_4, proof4));
}
Loading
Loading