From bbe6302fca0f03bca15392d059df1369c2e8ee65 Mon Sep 17 00:00:00 2001 From: yashm001 Date: Thu, 28 Sep 2023 00:35:25 +0530 Subject: [PATCH 01/22] initial commit --- Scarb.toml | 18 + src/Master.cairo | 498 ++++++++++++++++++ src/access.cairo | 1 + src/access/ownable.cairo | 104 ++++ src/lib.cairo | 2 + target/CACHEDIR.TAG | 3 + ...contributor_SBT2_0.starknet_artifacts.json | 1 + .../dev/contributor_SBT2_0_Master.casm.json | 1 + .../dev/contributor_SBT2_0_Master.sierra.json | 1 + version.txt | 3 + 10 files changed, 632 insertions(+) create mode 100644 Scarb.toml create mode 100644 src/Master.cairo create mode 100644 src/access.cairo create mode 100644 src/access/ownable.cairo create mode 100644 src/lib.cairo create mode 100644 target/CACHEDIR.TAG create mode 100644 target/dev/contributor_SBT2_0.starknet_artifacts.json create mode 100644 target/dev/contributor_SBT2_0_Master.casm.json create mode 100644 target/dev/contributor_SBT2_0_Master.sierra.json create mode 100644 version.txt diff --git a/Scarb.toml b/Scarb.toml new file mode 100644 index 0000000..dc7f79a --- /dev/null +++ b/Scarb.toml @@ -0,0 +1,18 @@ +[package] +name = "contributor_SBT2_0" +version = "0.1.0" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html +[[target.starknet-contract]] +sierra = true +casm = true +casm-add-pythonic-hints = true +allowed-libfuncs-list.name = "all" + +[workspace.dependencies] +alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git" } + +[dependencies] +starknet = "2.2.0" +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.5.0" } +alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git" } diff --git a/src/Master.cairo b/src/Master.cairo new file mode 100644 index 0000000..fba7573 --- /dev/null +++ b/src/Master.cairo @@ -0,0 +1,498 @@ +// @title Mesh Contributor SBTs Master Cairo 2.2 +// @author Mesh Finance +// @license MIT +// @notice Master to store contribution points + +use starknet::ContractAddress; +use zeroable::Zeroable; +use array::{Array, ArrayTrait, SpanTrait}; +use serde::Serde; +use traits::{Into, TryInto}; +use alexandria_storage::list::{List, ListTrait}; + + + +#[derive(Drop, Serde, starknet::Store)] +struct GuildPoints { + // @notice the cummulative score for each contributor + cum_score: u32, + // @notice Montly points for contribution for eg [Sept_2023 -> 250] will be written as [092023, 250] + // even index as month_id, immediate right is points in that month + data: List:: +} + +#[derive(Drop, Serde, starknet::Store)] +struct Contribution { + // @notice Contribution for dev guild + dev: GuildPoints, + // @notice Contribution for design guild + design: GuildPoints, + // @notice Contribution for problem solving guild + problem_solving: GuildPoints, + // @notice Contribution for marcom guild + marcom: GuildPoints, + // @notice Contribution for research guild + research: GuildPoints, + // @notice timestamp for the last update + last_timestamp: u64 +} + +#[derive(Copy, Drop, Serde, starknet::Store)] +struct MontlyContribution { + // @notice Contributor Address, used in update_contribution function + contributor: ContractAddress, + // @notice Contribution for dev guild + dev: u32, + // @notice Contribution for design guild + design: u32, + // @notice Contribution for problem solving guild + problem_solving: u32, + // @notice Contribution for marcom guild + marcom: u32, + // @notice Contribution for research guild + research: u32 + +} + +#[derive(Copy, Drop, Serde, starknet::Store)] +struct TotalMontlyContribution { + // @notice Montly contribution for dev guild + dev: u32, + // @notice Montly contribution for design guild + design: u32, + // @notice Montly contribution for problem solving guild + problem_solving: u32, + // @notice Montly contribution for marcom guild + marcom: u32, + // @notice Montly contribution for research guild + research: u32 +} + +// +// External Interfaces +// + + +#[starknet::interface] +trait IGuild { + fn migrate_sbt(ref self: T, old_address: ContractAddress, new_address: ContractAddress); +} + + +// +// Contract Interface +// +#[starknet::interface] +trait IMaster { + // view functions + fn get_contibutions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; + fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; + fn get_design_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; + fn get_problem_solving_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; + fn get_marcom_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; + fn get_research_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; + + // external functions + fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); + fn initialise(ref self: TContractState, dev_guild: ContractAddress, design_guild: ContractAddress, marcom_guild: ContractAddress, problem_solver_guild: ContractAddress, research_guild: ContractAddress); + fn migrate_points_initiated_by_DAO(ref self: TContractState, old_addresses: Array::, new_addresses: Array::); + fn migrate_points_initiated_by_holder(ref self: TContractState, new_address: ContractAddress); + fn execute_migrate_points_initiated_by_holder(ref self: TContractState, old_address: ContractAddress, new_address: ContractAddress); + + +} + + +#[starknet::contract] +mod Master { + use traits::Into; // TODO remove intos when u256 inferred type is available + use option::OptionTrait; + use array::{ArrayTrait, SpanTrait}; + use result::ResultTrait; + use zeroable::Zeroable; + use hash::LegacyHash; + use contributor_SBT2_0::access::ownable::{Ownable, IOwnable}; + use contributor_SBT2_0::access::ownable::Ownable::{ + ModifierTrait as OwnableModifierTrait, InternalTrait as OwnableInternalTrait, + }; + use starknet::{ContractAddress, ClassHash, SyscallResult, SyscallResultTrait, get_caller_address, get_contract_address, get_block_timestamp, contract_address_const}; + use integer::{u128_try_from_felt252, u256_sqrt, u256_from_felt252}; + use starknet::syscalls::{replace_class_syscall, call_contract_syscall}; + use alexandria_storage::list::{List, ListTrait}; + use super::{GuildPoints, Contribution, MontlyContribution, TotalMontlyContribution}; + use super::{ + IGuildDispatcher, IGuildDispatcherTrait + }; + + + // + // Storage Pair + // + #[storage] + struct Storage { + _contributions: LegacyMap::, // @dev contributions + _total_contribution: LegacyMap::, // @dev total contribution month wise [month_id => points] + _last_update_id: u32, // @dev contribution update id + _last_update_time: u64, // @dev timestamp for last update + _dev_guild_SBT: ContractAddress, // @dev contract address for dev guild SBTs + _design_guild_SBT: ContractAddress, // @dev contract address for design guild guild SBTs + _marcom_guild_SBT: ContractAddress, // @dev contract address for marcom guild SBTs + _problem_solving_guild_SBT: ContractAddress, // @dev contract address for problem solving guild SBTs + _research_guild_SBT: ContractAddress, // @dev contract address for research guild SBTs + _initialised: u8, // @dev Flag to store initialisation state + _queued_migrations: LegacyMap::, // @dev flag to store queued migration requests. + // _klast: u256, // @dev reserve0 * reserve1, as of immediately after the most recent liquidity event + // _locked: bool, // @dev Boolean to check reentrancy + // _factory: ContractAddress, // @dev Factory contract address + // Proxy_admin: ContractAddress, // @dev Admin contract address, to be used till we finalize Cairo upgrades. + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + ContributionUpdated: ContributionUpdated, + MigrationQueued: MigrationQueued, + Migrated: Migrated, + // Swap: Swap, + // Sync: Sync + } + + // @notice An event emitted whenever contribution is updated + #[derive(Drop, starknet::Event)] + struct ContributionUpdated { + update_id: u32, + contributor: ContractAddress, + month_id: u32, + points_earned: MontlyContribution + } + + // @notice An event emitted whenever migration is queued + #[derive(Drop, starknet::Event)] + struct MigrationQueued { + old_address: ContractAddress, + new_address: ContractAddress, + hash: felt252 + } + + // @notice An event emitted whenever SBT is migrated + #[derive(Drop, starknet::Event)] + struct Migrated { + old_address: ContractAddress, + new_address: ContractAddress + } + + + // + // Constructor + // + + // @notice Contract constructor + #[constructor] + fn constructor(ref self: ContractState, owner_: ContractAddress,) { + // @notice not sure if default is already zero or need to initialise. + self._last_update_id.write(0_u32); + self._last_update_time.write(0_u64); + self._initialised.write(0_u8); + + let mut ownable_self = Ownable::unsafe_new_contract_state(); + ownable_self._transfer_ownership(new_owner: owner_); + + } + + #[external(v0)] + impl Master of super::IMaster { + // + // Getters + // + fn get_contibutions_points(self: @ContractState, contributor: ContractAddress) -> Contribution { + self._contributions.read(contributor) + } + + fn get_dev_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { + let contribution = self._contributions.read(contributor); + contribution.dev + } + + fn get_dev_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { + let contribution = self._contributions.read(contributor); + contribution.dev + } + + + // + // Setters + // + + fn initialise(ref self: ContractState, dev_guild: ContractAddress, design_guild: ContractAddress, marcom_guild: ContractAddress, problem_solver_guild: ContractAddress, research_guild: ContractAddress) { + self._only_owner(); + let is_initialised = self._initialised.read(); + assert (is_initialised == 0, "ALREADY_INITIALISED"); + + self._dev_guild_SBT.write(dev_guild); + self._design_guild_SBT.write(design_guild); + self._marcom_guild_SBT.write(marcom_guild); + self._problem_solving_guild_SBT.write(problem_solver_guild); + self._research_guild_SBT.write(research_guild); + self._initialised.write(1_u8); + } + + fn update_contibutions(ref self: ContractState, month_id: u32, contributions: Array::) { + self._only_owner(); + let block_timestamp = get_block_timestamp(); + let id = self._last_update_id.read(); + let mut current_index = 0; + + // for keeping track of cummulative guild points for that month. + let mut dev_total_cum = 0.into(); + let mut design_total_cum = 0.into(); + let mut problem_solving_total_cum = 0.into(); + let mut marcom_total_cum = 0.into(); + let mut research_total_cum = 0.into(); + + loop { + if (current_index == contributions.len() - 1) { + break true; + } + let new_contributions = *contributions[current_index]; + let contributor:ContractAddress = new_contributions.contributor; + // let points = new_contributions.points; + // let mut points_index = 0; + + // loop { + // if (points_index == 4) { + // break true; + // } + // let contribution_point = *points[points_index]; + // if (contribution_point == 0) { + // continue true; + // } + // } + + let old_contribution = self._contributions.read(contributor); + let mut list:ListTrait = ListTrait::new(); + + let old_dev_contribution = old_contribution.dev; + let contribution_data_dev = old_dev_contribution.data; + let mut cum_score_dev = old_dev_contribution.cum_score; + if (new_contributions.dev != 0) { + + list.append(contribution_data_dev, month_id); + list.append(contribution_data_dev, new_contributions.dev); + + cum_score_dev = cum_score_dev + new_contributions.dev; + + dev_total_cum = dev_total_cum + new_contributions.dev; + } + let new_dev_contribution = GuildPoints{cum_score: cum_score_dev, data: contribution_data_dev}; + + + let old_design_contribution = old_contribution.design; + let contribution_data_design = old_design_contribution.data; + let mut cum_score_design = old_design_contribution.cum_score; + if (new_contributions.design != 0) { + + list.append(contribution_data_design, month_id); + list.append(contribution_data_design, new_contributions.design); + // contribution_data_design.append(month_id); + // contribution_data_design.append(new_contributions.design); + + cum_score_design = cum_score_design + new_contributions.design; + + design_total_cum = design_total_cum + new_contributions.design; + } + let new_design_contribution = GuildPoints{cum_score: cum_score_design, data: contribution_data_design}; + + + let old_problem_solving_contribution = old_contribution.problem_solving; + let contribution_data_problem_solving = old_problem_solving_contribution.data; + let mut cum_score_problem_solving = old_problem_solving_contribution.cum_score; + if (new_contributions.problem_solving != 0) { + + list.append(contribution_data_problem_solving, month_id); + list.append(contribution_data_problem_solving, new_contributions.problem_solving); + // contribution_data_problem_solving.append(month_id); + // contribution_data_problem_solving.append(new_contributions.problem_solving); + + cum_score_problem_solving = cum_score_problem_solving + new_contributions.problem_solving; + + problem_solving_total_cum = problem_solving_total_cum + new_contributions.problem_solving; + } + let new_problem_solving_contribution = GuildPoints{cum_score: cum_score_problem_solving, data: contribution_data_problem_solving}; + + + let old_marcom_contribution = old_contribution.marcom; + let contribution_data_marcom = old_marcom_contribution.data; + let mut cum_score_marcom = old_marcom_contribution.cum_score; + if (new_contributions.marcom != 0) { + + list.append(contribution_data_marcom, month_id); + list.append(contribution_data_marcom, new_contributions.marcom); + // contribution_data_marcom.append(month_id); + // contribution_data_marcom.append(new_contributions.marcom); + + cum_score_marcom = cum_score_marcom + new_contributions.marcom; + marcom_total_cum = marcom_total_cum + new_contributions.marcom; + } + let new_marcom_contribution = GuildPoints{cum_score: cum_score_marcom, data: contribution_data_marcom}; + + + let old_research_contribution = old_contribution.research; + let contribution_data_research = old_research_contribution.data; + let mut cum_score_research = old_research_contribution.cum_score; + if (new_contributions.research != 0) { + + list.append(contribution_data_research, month_id); + list.append(contribution_data_research, new_contributions.research); + // contribution_data_research.append(month_id); + // contribution_data_research.append(new_contributions.research); + + cum_score_research = cum_score_research + new_contributions.research; + + research_total_cum = research_total_cum + new_contributions.research; + } + let new_research_contribution = GuildPoints{cum_score: cum_score_research, data: contribution_data_research}; + + + let updated_contribution = Contribution{dev: new_dev_contribution, design: new_design_contribution, problem_solving: new_problem_solving_contribution, marcom: new_marcom_contribution, research: new_research_contribution, last_timestamp: block_timestamp}; + self._contributions.write(contributor, updated_contribution); + + let total_monthy_contribution = TotalMontlyContribution{dev: dev_total_cum, design: design_total_cum, problem_solving: problem_solving_total_cum, marcom: marcom_total_cum, research: research_total_cum}; + self._total_contribution.write(month_id, total_monthy_contribution); + current_index += 1; + // } + self.emit(ContributionUpdated{update_id: id, contributor: contributor, month_id: month_id, points_earned: new_contributions}); + + }; + id += 1; + self._last_update_id.write(id); + + } + + + fn migrate_points_initiated_by_DAO(ref self: ContractState, old_addresses: Array::, new_addresses: Array:: ) { + self._only_owner(); + assert(old_addresses.len() == new_addresses.len(), "INVALID_INPUTS"); + let mut current_index = 0; + + loop { + if (current_index == old_addresses.len() - 1) { + break true; + } + InternalImpl::_migrate_points(ref self, *old_addresses[current_index], *new_addresses[current_index]); + current_index += 1; + }; + + } + + + fn migrate_points_initiated_by_holder(ref self: ContractState, new_address: ContractAddress) { + let caller = get_caller_address(); + let migration_hash: felt252 = LegacyHash::hash(caller.into(), new_address); + + self._queued_migrations.write(migration_hash, 1_u8); + + self.emit(MigrationQueued { old_address: caller, new_address: new_address, hash: migration_hash}); + + } + + fn execute_migrate_points_initiated_by_holder(ref self: ContractState, old_address: ContractAddress, new_address: ContractAddress) { + self._only_owner(); + let migration_hash: felt252 = LegacyHash::hash(old_address.into(), new_address); + let is_queued = self._queued_migrations.read(migration_hash); + + assert(is_queued == 1, "NOT_QUEUED"); + + self._queued_migrations.write(migration_hash, 0_u8); + InternalImpl::_migrate_points(ref self, old_address, new_address); + + } + + + + + } + + #[generate_trait] + impl InternalImpl of InternalTrait { + // + // Internals + // + + fn _migrate_points(ref self: ContractState, old_address: ContractAddress, new_address: ContractAddress) { + + let design_guild = self._design_guild_SBT.read(); + let dev_guild = self._dev_guild_SBT.read(); + let problem_solver_guild = self._problem_solving_guild_SBT.read(); + let marcom_guild = self._marcom_guild_SBT.read(); + let research_guild = self._research_guild_SBT.read(); + + let contribution = self._contributions.read(old_address); + + let zero_contribution = Contribution{dev: GuildPoints{ cum_score: 0_u32, data: ListTrait::new()}, + design: GuildPoints{ cum_score: 0_u32, data: ListTrait::new()}, + problem_solving: GuildPoints{ cum_score: 0_u32, data: ListTrait::new()}, + marcom: GuildPoints{ cum_score: 0_u32, data: ListTrait::new()}, + research: GuildPoints{ cum_score: 0_u32, data: ListTrait::new()}, + last_timestamp: 0_u64 + }; + + self._contributions.write(old_address, zero_contribution); + self._contributions.write(new_address, contribution); + + let dev_guildDispatcher = IGuildDispatcher { contract_address: dev_guild }; + dev_guildDispatcher.migrate_sbt(old_address, new_address); + + let design_guildDispatcher = IGuildDispatcher { contract_address: design_guild }; + design_guildDispatcher.migrate_sbt(old_address, new_address); + + let problem_solver_guildDispatcher = IGuildDispatcher { contract_address: problem_solver_guild }; + problem_solver_guildDispatcher.migrate_sbt(old_address, new_address); + + let marcom_guildDispatcher = IGuildDispatcher { contract_address: marcom_guild }; + marcom_guildDispatcher.migrate_sbt(old_address, new_address); + + let research_guildDispatcher = IGuildDispatcher { contract_address: research_guild }; + research_guildDispatcher.migrate_sbt(old_address, new_address); + + self.emit(Migrated{old_address: old_address, new_address: new_address}); + + } + + } + + + + #[generate_trait] + impl ModifierImpl of ModifierTrait { + fn _only_owner(self: @ContractState) { + let mut ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.assert_only_owner(); + } + } + + #[external(v0)] + impl IOwnableImpl of IOwnable { + fn owner(self: @ContractState) -> ContractAddress { + let ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.owner() + } + + fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { + let mut ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.transfer_ownership(:new_owner); + } + + fn renounce_ownership(ref self: ContractState) { + let mut ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.renounce_ownership(); + } + } + + + +} + diff --git a/src/access.cairo b/src/access.cairo new file mode 100644 index 0000000..06dd03e --- /dev/null +++ b/src/access.cairo @@ -0,0 +1 @@ +mod ownable; \ No newline at end of file diff --git a/src/access/ownable.cairo b/src/access/ownable.cairo new file mode 100644 index 0000000..065e8c4 --- /dev/null +++ b/src/access/ownable.cairo @@ -0,0 +1,104 @@ +use starknet::ContractAddress; + +#[starknet::interface] +trait IOwnable { + fn owner(self: @TContractState) -> ContractAddress; + + fn transfer_ownership(ref self: TContractState, new_owner: ContractAddress); + + fn renounce_ownership(ref self: TContractState); +} + +#[starknet::contract] +mod Ownable { + use zeroable::Zeroable; + + // // locals + use contributor_SBT2_0::access::ownable; + use starknet::ContractAddress; + use starknet::get_caller_address; + + // + // Storage + // + + #[storage] + struct Storage { + _owner: ContractAddress + } + + // + // Events + // + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + OwnershipTransferred: OwnershipTransferred, + } + + #[derive(Drop, starknet::Event)] + struct OwnershipTransferred { + previous_owner: ContractAddress, + new_owner: ContractAddress, + } + + // + // Modifiers + // + + #[generate_trait] + impl ModifierImpl of ModifierTrait { + fn assert_only_owner(self: @ContractState) { + let owner = self._owner.read(); + let caller = get_caller_address(); + assert(!caller.is_zero(), 'Caller is the zero address'); + assert(caller == owner, 'Caller is not the owner'); + } + } + + // + // Ownable impl + // + + #[external(v0)] + impl IOwnableImpl of ownable::IOwnable { + fn owner(self: @ContractState) -> ContractAddress { + self._owner.read() + } + + fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { + assert(!new_owner.is_zero(), 'New owner is the zero address'); + self.assert_only_owner(); + self._transfer_ownership(new_owner); + } + + fn renounce_ownership(ref self: ContractState) { + self.assert_only_owner(); + self._transfer_ownership(Zeroable::zero()); + } + } + + // + // Internals + // + + #[generate_trait] + impl InternalImpl of InternalTrait { + fn initializer(ref self: ContractState) { + let caller = get_caller_address(); + self._transfer_ownership(caller); + } + + fn _transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { + let previous_owner = self._owner.read(); + self._owner.write(new_owner); + + // Events + self + .emit( + Event::OwnershipTransferred(OwnershipTransferred { previous_owner, new_owner }) + ); + } + } +} \ No newline at end of file diff --git a/src/lib.cairo b/src/lib.cairo new file mode 100644 index 0000000..81421e9 --- /dev/null +++ b/src/lib.cairo @@ -0,0 +1,2 @@ +mod Master; +mod access; \ No newline at end of file diff --git a/target/CACHEDIR.TAG b/target/CACHEDIR.TAG new file mode 100644 index 0000000..e95ca71 --- /dev/null +++ b/target/CACHEDIR.TAG @@ -0,0 +1,3 @@ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by scarb. +# For information about cache directory tags see https://bford.info/cachedir/ diff --git a/target/dev/contributor_SBT2_0.starknet_artifacts.json b/target/dev/contributor_SBT2_0.starknet_artifacts.json new file mode 100644 index 0000000..5e6b3a1 --- /dev/null +++ b/target/dev/contributor_SBT2_0.starknet_artifacts.json @@ -0,0 +1 @@ +{"version":1,"contracts":[{"id":"0af7eom6gavt2","package_name":"contributor_SBT2_0","contract_name":"Master","artifacts":{"sierra":"contributor_SBT2_0_Master.sierra.json","casm":"contributor_SBT2_0_Master.casm.json"}}]} \ No newline at end of file diff --git a/target/dev/contributor_SBT2_0_Master.casm.json b/target/dev/contributor_SBT2_0_Master.casm.json new file mode 100644 index 0000000..4d920ac --- /dev/null +++ b/target/dev/contributor_SBT2_0_Master.casm.json @@ -0,0 +1 @@ +{"prime":"0x800000000000011000000000000000000000000000000000000000000000001","compiler_version":"2.2.0","bytecode":["0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x49","0x4825800180007ffa","0x0","0x400280007ff97fff","0x48297ffc80007ffd","0x482680017ff98000","0x1","0x4824800180007ffe","0x0","0x20680017fff7fff","0x4","0x10780017fff7fff","0x10","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ff97fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3d","0x482480017fff8000","0x3c","0x480080007fff8000","0xa0680017fff8000","0x9","0x4824800180007ff7","0x0","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff77fff","0x10780017fff7fff","0x12","0x4824800180007ff7","0x0","0x400080007ff87fff","0x1104800180018000","0x2b","0x40780017fff7fff","0x1","0x482480017ff58000","0x1","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff58000","0x1","0x48127ff27fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x208b7fff7fff7ffe"],"hints":[[0,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[19,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[38,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"AP","offset":-8}},"dst":{"register":"AP","offset":0}}}]],[52,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[63,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[78,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]]],"pythonic_hints":[[0,["memory[ap + 0] = 0 <= memory[fp + -6]"]],[19,["memory[ap + 0] = segments.add()"]],[38,["memory[ap + 0] = 0 <= memory[ap + -8]"]],[52,["memory[ap + 0] = segments.add()"]],[63,["memory[ap + 0] = segments.add()"]],[78,["memory[ap + 0] = segments.add()"]]],"entry_points_by_type":{"EXTERNAL":[],"L1_HANDLER":[],"CONSTRUCTOR":[{"selector":"0x28ffe4ff0f226a9107253e17a904099aa4f63a02a5621de0576e5aa71bc5194","offset":0,"builtins":["range_check"]}]}} \ No newline at end of file diff --git a/target/dev/contributor_SBT2_0_Master.sierra.json b/target/dev/contributor_SBT2_0_Master.sierra.json new file mode 100644 index 0000000..2650ec5 --- /dev/null +++ b/target/dev/contributor_SBT2_0_Master.sierra.json @@ -0,0 +1 @@ +{"sierra_program":["0x1","0x3","0x0","0x2","0x2","0x0","0x75","0x8b","0x11","0x52616e6765436865636b","0x800000000000000100000000000000000000000000000000","0x4172726179","0x800000000000000300000000000000000000000000000001","0x1","0xe","0x536e617073686f74","0x800000000000000700000000000000000000000000000001","0x537472756374","0x800000000000000700000000000000000000000000000002","0x0","0x1baeba72e79e9db2587cf44fedb2f3700b2075a5e8e39a562584862c4b71f62","0x2","0x2ee1e2b1b89f8c495f200e4956278a4d47395fe262f27b52e5865c9524c08c3","0x3","0x800000000000000f00000000000000000000000000000001","0x3bc084d3c7617fdd6c5518880820319a137a8a7b477b020610b85a6219edb83","0x1835d756ce4c2f042b51de151fbb313a64e1d594b276e47db4ef4ee71c80f50","0x800000000000000f00000000000000000000000000000003","0x2d78d0f1a11ade2f938c53bc8c2a5f956d376203245eccaf880bf121d4764cf","0x6","0x7","0x4275696c74696e436f737473","0x800000000000000700000000000000000000000000000000","0x53797374656d","0x16a4c8d7c05909052238a862d8cc3e7975bf05a07b3a69c6b28951083a6d672","0x800000000000000300000000000000000000000000000003","0xb","0x456e756d","0x9931c641b913035ae674b400b61a51476d506bbe8bba2ff8a6272790aba9e6","0x4","0xc","0x66656c74323532","0x753332","0x4761734275696c74696e","0x29","0x7265766f6b655f61705f747261636b696e67","0x77697468647261775f676173","0x6272616e63685f616c69676e","0x7374727563745f6465636f6e737472756374","0x61727261795f6c656e","0x736e617073686f745f74616b65","0xf","0x64726f70","0x7533325f636f6e7374","0x72656e616d65","0x73746f72655f74656d70","0x7533325f6571","0x61727261795f6e6577","0x66656c743235325f636f6e7374","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x61727261795f617070656e64","0x7374727563745f636f6e737472756374","0x656e756d5f696e6974","0xd","0x10","0xa","0x6765745f6275696c74696e5f636f737473","0x9","0x77697468647261775f6761735f616c6c","0x8","0x66756e6374696f6e5f63616c6c","0x5","0x4f7574206f6620676173","0x52","0xffffffffffffffff","0x40","0x1c","0x12","0x13","0x14","0x15","0x16","0x17","0x18","0x19","0x1a","0x1b","0x1d","0x33","0x1e","0x1f","0x20","0x21","0x22","0x25","0x23","0x24","0x26","0x27","0x28","0x2a","0x2b","0x2c","0x2d","0x2e","0x2f","0x30","0x31","0x32","0x34","0x35","0x36","0x37","0x38","0x39","0x3a","0x3b","0x3c","0x3d","0x3e","0x3f","0x41","0x42","0x43","0x4e","0x379","0x110b10090e0b10090f050e0b0a090d050c0b0a090505080706050403020100","0xb1b1d05051c050e0b1b091a0b10090219181716051505140b1309120b1009","0x2c2b05052a060505290f0505280b270b260b252402231822182120051f051e","0x52f330d0532060505310b300b05052f2b05052f2b05052e0b0d052d2b0505","0x5052f3905052f3805052f050d37050d36200505351c050535060505340605","0x52c050f053e3d05052f3d05053516050535150505350b3c3b05052f0b3a37","0xd05320b0d37050d361f0505350f0505350505052c0505052a3f05052c3d05","0x433f1f0d420d050b0d050b0b42050b0b0b413f05052f3f0505350f05052c40","0x42053b053f0b3b0542053d051f0b3d0542050f050f0b0b42050b0d0b16150d","0x53705150b06370d420520053f0b200542050b160b0b42053905150b1c390d","0x51f05390b2b0542052b053b0b3805420506053d0b2b0542051c053d0b0b42","0x42050b370b000542050b200b0b42050b0d0b0b440b420d382b0d1c0b1f0542","0x46470d000b470542050b380b4605420545000d2b0b450542054505060b4505","0x5470b4b0542053f05460b4a0542051f05390b490542054805450b48054205","0x542050b490b0b42050b0d0b4d4c4b4a1f054d0542054905480b4c0542050d","0xb0b42050b0d0b52510d504f440d420d4e3f1f0f4b0b4e0542054e054a0b4e","0xb560542055505440b5505420554530d4e0b540542050b4d0b530542050b4c","0x530b590542050b200b0b42055805520b0b42055705510b58570d420556054f","0xb5c0542052405570b240542055b05550b0b42055a05540b5b5a0d42055905","0x600542050d05470b5f0542054f05460b5e0542054405390b5d0542055c0558","0x50b560b620542050b200b0b42050b0d0b61605f5e1f05610542055d05480b","0x500d000b500542050b380b6405420563620d2b0b630542056305060b630542","0x470b680542055205460b670542055105390b660542056505450b6505420564","0x50f05590b0b42050b0d0b6a6968671f056a0542056605480b690542050d05","0x42056c6b0d2b0b6c0542056c05060b6c0542050b560b6b0542050b200b0b42","0x51505390b700542056f05450b6f0542056d6e0d000b6e0542050b380b6d05","0x71431f05730542057005480b720542050d05470b710542051605460b430542","0x1f0d0f0d0d050f05420505055b0b0d0542050b05440b050542050b5a0b7372","0x740b3f3d0d3d050b0f0d050b3739380b1f0f39380b"],"sierra_program_debug_info":{"type_names":[[0,"RangeCheck"],[1,"Array"],[2,"Snapshot>"],[3,"core::array::Span::"],[4,"Tuple>"],[5,"Unit"],[6,"contributor_SBT2_0::Master::Master::_contributions::ContractMemberState"],[7,"contributor_SBT2_0::Master::Master::_total_contribution::ContractMemberState"],[8,"contributor_SBT2_0::Master::Master::ContractState"],[9,"BuiltinCosts"],[10,"System"],[11,"core::panics::Panic"],[12,"Tuple>"],[13,"core::panics::PanicResult::<(core::array::Span::,)>"],[14,"felt252"],[15,"u32"],[16,"GasBuiltin"]],"libfunc_names":[[0,"revoke_ap_tracking"],[1,"withdraw_gas"],[2,"branch_align"],[3,"struct_deconstruct>"],[4,"array_len"],[5,"snapshot_take"],[6,"drop"],[7,"u32_const<0>"],[8,"rename"],[9,"store_temp"],[10,"store_temp"],[11,"u32_eq"],[12,"array_new"],[13,"felt252_const<7733229381460288120802334208475838166080759535023995805565484692595>"],[14,"store_temp"],[15,"array_append"],[16,"struct_construct"],[17,"struct_construct>>"],[18,"enum_init,)>, 1>"],[19,"store_temp"],[20,"store_temp"],[21,"store_temp,)>>"],[22,"get_builtin_costs"],[23,"store_temp"],[24,"withdraw_gas_all"],[25,"struct_construct"],[26,"struct_construct"],[27,"struct_construct"],[28,"store_temp"],[29,"function_call"],[30,"drop"],[31,"drop"],[32,"snapshot_take>"],[33,"drop>"],[34,"struct_construct>"],[35,"struct_construct>>"],[36,"enum_init,)>, 0>"],[37,"felt252_const<375233589013918064796019>"],[38,"drop>"],[39,"struct_construct"],[40,"store_temp"]],"user_func_names":[[0,"contributor_SBT2_0::Master::Master::__wrapper_constructor"],[1,"contributor_SBT2_0::Master::Master::constructor"]]},"contract_class_version":"0.1.0","entry_points_by_type":{"EXTERNAL":[],"L1_HANDLER":[],"CONSTRUCTOR":[{"selector":"0x28ffe4ff0f226a9107253e17a904099aa4f63a02a5621de0576e5aa71bc5194","function_idx":0}]},"abi":[{"type":"impl","name":"Master","interface_name":"contributor_SBT2_0::Master::IMaster"},{"type":"interface","name":"contributor_SBT2_0::Master::IMaster","items":[]},{"type":"constructor","name":"constructor","inputs":[]},{"type":"event","name":"contributor_SBT2_0::Master::Master::Event","kind":"enum","variants":[]}]} \ No newline at end of file diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..c3e75aa --- /dev/null +++ b/version.txt @@ -0,0 +1,3 @@ +scarb 0.7.0 (58cc88efb 2023-08-23) +cairo: 2.2.0 (https://crates.io/crates/cairo-lang-compiler/2.2.0) +sierra: 1.3.0 From 6b503456bf997cea77c28cc8800a6b10f73470cf Mon Sep 17 00:00:00 2001 From: yashm001 Date: Thu, 28 Sep 2023 22:29:25 +0530 Subject: [PATCH 02/22] Update Master.cairo --- src/Master.cairo | 144 +++++++++++++++++++++++++++++++---------------- 1 file changed, 94 insertions(+), 50 deletions(-) diff --git a/src/Master.cairo b/src/Master.cairo index fba7573..3abb52c 100644 --- a/src/Master.cairo +++ b/src/Master.cairo @@ -91,6 +91,15 @@ trait IMaster { fn get_problem_solving_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; fn get_marcom_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; fn get_research_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; + fn get_last_update_id(self: @TContractState) -> u32; + fn get_last_update_time(self: @TContractState) -> u64; + fn get_migartion_queued_state(self: @TContractState, hash: felt252 ) -> bool; + fn get_dev_guild_SBT(self: @TContractState) -> ContractAddress; + fn get_design_guild_SBT(self: @TContractState) -> ContractAddress; + fn get_marcom_guild_SBT(self: @TContractState) -> ContractAddress; + fn get_problem_solving_guild_SBT(self: @TContractState) -> ContractAddress; + fn get_research_guild_SBT(self: @TContractState) -> ContractAddress; + // external functions fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); @@ -139,12 +148,8 @@ mod Master { _marcom_guild_SBT: ContractAddress, // @dev contract address for marcom guild SBTs _problem_solving_guild_SBT: ContractAddress, // @dev contract address for problem solving guild SBTs _research_guild_SBT: ContractAddress, // @dev contract address for research guild SBTs - _initialised: u8, // @dev Flag to store initialisation state - _queued_migrations: LegacyMap::, // @dev flag to store queued migration requests. - // _klast: u256, // @dev reserve0 * reserve1, as of immediately after the most recent liquidity event - // _locked: bool, // @dev Boolean to check reentrancy - // _factory: ContractAddress, // @dev Factory contract address - // Proxy_admin: ContractAddress, // @dev Admin contract address, to be used till we finalize Cairo upgrades. + _initialised: bool, // @dev Flag to store initialisation state + _queued_migrations: LegacyMap::, // @dev flag to store queued migration requests. } #[event] @@ -192,7 +197,7 @@ mod Master { // @notice not sure if default is already zero or need to initialise. self._last_update_id.write(0_u32); self._last_update_time.write(0_u64); - self._initialised.write(0_u8); + self._initialised.write(false); let mut ownable_self = Ownable::unsafe_new_contract_state(); ownable_self._transfer_ownership(new_owner: owner_); @@ -213,9 +218,56 @@ mod Master { contribution.dev } - fn get_dev_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { + fn get_design_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { let contribution = self._contributions.read(contributor); - contribution.dev + contribution.design + } + + fn get_problem_solving_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { + let contribution = self._contributions.read(contributor); + contribution.problem_solving + } + + fn get_marcom_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { + let contribution = self._contributions.read(contributor); + contribution.marcom + } + + fn get_research_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { + let contribution = self._contributions.read(contributor); + contribution.research + } + + fn get_last_update_id(self: @ContractState) -> u32 { + self._last_update_id.read() + } + + fn get_last_update_time(self: @ContractState) -> u64 { + self._last_update_time.read() + } + + fn get_migartion_queued_state(self: @ContractState, hash: felt252 ) -> bool { + self._queued_migrations.read(hash) + } + + fn get_dev_guild_SBT(self: @ContractState) -> ContractAddress { + self._dev_guild_SBT.read() + } + + fn get_design_guild_SBT(self: @ContractState) -> ContractAddress { + self._design_guild_SBT.read() + } + + fn get_marcom_guild_SBT(self: @ContractState) -> ContractAddress { + self._marcom_guild_SBT.read() + } + + fn get_problem_solving_guild_SBT(self: @ContractState) -> ContractAddress { + self._problem_solving_guild_SBT.read() + } + + fn get_research_guild_SBT(self: @ContractState) -> ContractAddress { + self._research_guild_SBT.read() } @@ -226,35 +278,35 @@ mod Master { fn initialise(ref self: ContractState, dev_guild: ContractAddress, design_guild: ContractAddress, marcom_guild: ContractAddress, problem_solver_guild: ContractAddress, research_guild: ContractAddress) { self._only_owner(); let is_initialised = self._initialised.read(); - assert (is_initialised == 0, "ALREADY_INITIALISED"); + assert (is_initialised == false, "ALREADY_INITIALISED"); self._dev_guild_SBT.write(dev_guild); self._design_guild_SBT.write(design_guild); self._marcom_guild_SBT.write(marcom_guild); self._problem_solving_guild_SBT.write(problem_solver_guild); self._research_guild_SBT.write(research_guild); - self._initialised.write(1_u8); + self._initialised.write(true); } fn update_contibutions(ref self: ContractState, month_id: u32, contributions: Array::) { self._only_owner(); let block_timestamp = get_block_timestamp(); - let id = self._last_update_id.read(); + let mut id = self._last_update_id.read(); let mut current_index = 0; // for keeping track of cummulative guild points for that month. - let mut dev_total_cum = 0.into(); - let mut design_total_cum = 0.into(); - let mut problem_solving_total_cum = 0.into(); - let mut marcom_total_cum = 0.into(); - let mut research_total_cum = 0.into(); + let mut dev_total_cum = 0_u32; + let mut design_total_cum = 0_u32; + let mut problem_solving_total_cum = 0_u32; + let mut marcom_total_cum = 0_u32; + let mut research_total_cum = 0_u32; loop { if (current_index == contributions.len() - 1) { break true; } let new_contributions = *contributions[current_index]; - let contributor:ContractAddress = new_contributions.contributor; + let contributor: ContractAddress = new_contributions.contributor; // let points = new_contributions.points; // let mut points_index = 0; @@ -269,15 +321,14 @@ mod Master { // } let old_contribution = self._contributions.read(contributor); - let mut list:ListTrait = ListTrait::new(); let old_dev_contribution = old_contribution.dev; - let contribution_data_dev = old_dev_contribution.data; + let mut contribution_data_dev = old_dev_contribution.data; let mut cum_score_dev = old_dev_contribution.cum_score; if (new_contributions.dev != 0) { - list.append(contribution_data_dev, month_id); - list.append(contribution_data_dev, new_contributions.dev); + contribution_data_dev.append(month_id); + contribution_data_dev.append(new_contributions.dev); cum_score_dev = cum_score_dev + new_contributions.dev; @@ -287,14 +338,12 @@ mod Master { let old_design_contribution = old_contribution.design; - let contribution_data_design = old_design_contribution.data; + let mut contribution_data_design = old_design_contribution.data; let mut cum_score_design = old_design_contribution.cum_score; if (new_contributions.design != 0) { - list.append(contribution_data_design, month_id); - list.append(contribution_data_design, new_contributions.design); - // contribution_data_design.append(month_id); - // contribution_data_design.append(new_contributions.design); + contribution_data_design.append(month_id); + contribution_data_design.append(new_contributions.design); cum_score_design = cum_score_design + new_contributions.design; @@ -304,14 +353,12 @@ mod Master { let old_problem_solving_contribution = old_contribution.problem_solving; - let contribution_data_problem_solving = old_problem_solving_contribution.data; + let mut contribution_data_problem_solving = old_problem_solving_contribution.data; let mut cum_score_problem_solving = old_problem_solving_contribution.cum_score; if (new_contributions.problem_solving != 0) { - list.append(contribution_data_problem_solving, month_id); - list.append(contribution_data_problem_solving, new_contributions.problem_solving); - // contribution_data_problem_solving.append(month_id); - // contribution_data_problem_solving.append(new_contributions.problem_solving); + contribution_data_problem_solving.append(month_id); + contribution_data_problem_solving.append(new_contributions.problem_solving); cum_score_problem_solving = cum_score_problem_solving + new_contributions.problem_solving; @@ -321,14 +368,12 @@ mod Master { let old_marcom_contribution = old_contribution.marcom; - let contribution_data_marcom = old_marcom_contribution.data; + let mut contribution_data_marcom = old_marcom_contribution.data; let mut cum_score_marcom = old_marcom_contribution.cum_score; if (new_contributions.marcom != 0) { - list.append(contribution_data_marcom, month_id); - list.append(contribution_data_marcom, new_contributions.marcom); - // contribution_data_marcom.append(month_id); - // contribution_data_marcom.append(new_contributions.marcom); + contribution_data_marcom.append(month_id); + contribution_data_marcom.append(new_contributions.marcom); cum_score_marcom = cum_score_marcom + new_contributions.marcom; marcom_total_cum = marcom_total_cum + new_contributions.marcom; @@ -337,14 +382,12 @@ mod Master { let old_research_contribution = old_contribution.research; - let contribution_data_research = old_research_contribution.data; + let mut contribution_data_research = old_research_contribution.data; let mut cum_score_research = old_research_contribution.cum_score; if (new_contributions.research != 0) { - list.append(contribution_data_research, month_id); - list.append(contribution_data_research, new_contributions.research); - // contribution_data_research.append(month_id); - // contribution_data_research.append(new_contributions.research); + contribution_data_research.append(month_id); + contribution_data_research.append(new_contributions.research); cum_score_research = cum_score_research + new_contributions.research; @@ -365,6 +408,7 @@ mod Master { }; id += 1; self._last_update_id.write(id); + self._last_update_time.write(block_timestamp); } @@ -389,7 +433,7 @@ mod Master { let caller = get_caller_address(); let migration_hash: felt252 = LegacyHash::hash(caller.into(), new_address); - self._queued_migrations.write(migration_hash, 1_u8); + self._queued_migrations.write(migration_hash, true); self.emit(MigrationQueued { old_address: caller, new_address: new_address, hash: migration_hash}); @@ -400,9 +444,9 @@ mod Master { let migration_hash: felt252 = LegacyHash::hash(old_address.into(), new_address); let is_queued = self._queued_migrations.read(migration_hash); - assert(is_queued == 1, "NOT_QUEUED"); + assert(is_queued == true, "NOT_QUEUED"); - self._queued_migrations.write(migration_hash, 0_u8); + self._queued_migrations.write(migration_hash, false); InternalImpl::_migrate_points(ref self, old_address, new_address); } @@ -428,11 +472,11 @@ mod Master { let contribution = self._contributions.read(old_address); - let zero_contribution = Contribution{dev: GuildPoints{ cum_score: 0_u32, data: ListTrait::new()}, - design: GuildPoints{ cum_score: 0_u32, data: ListTrait::new()}, - problem_solving: GuildPoints{ cum_score: 0_u32, data: ListTrait::new()}, - marcom: GuildPoints{ cum_score: 0_u32, data: ListTrait::new()}, - research: GuildPoints{ cum_score: 0_u32, data: ListTrait::new()}, + let zero_contribution = Contribution{dev: GuildPoints{ cum_score: 0_u32, data: List::}, + design: GuildPoints{ cum_score: 0_u32, data: List::}, + problem_solving: GuildPoints{ cum_score: 0_u32, data: List::}, + marcom: GuildPoints{ cum_score: 0_u32, data: List::}, + research: GuildPoints{ cum_score: 0_u32, data: List::}, last_timestamp: 0_u64 }; From d0e4dc7c315edec9e61920393413c453eb9c70d9 Mon Sep 17 00:00:00 2001 From: yashm001 Date: Sat, 30 Sep 2023 20:43:36 +0530 Subject: [PATCH 03/22] Update Master.cairo --- src/Master.cairo | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Master.cairo b/src/Master.cairo index 3abb52c..3799822 100644 --- a/src/Master.cairo +++ b/src/Master.cairo @@ -16,7 +16,7 @@ use alexandria_storage::list::{List, ListTrait}; struct GuildPoints { // @notice the cummulative score for each contributor cum_score: u32, - // @notice Montly points for contribution for eg [Sept_2023 -> 250] will be written as [092023, 250] + // @notice Monthly points for contribution for eg [Sept_2023 -> 250] will be written as [092023, 250] // even index as month_id, immediate right is points in that month data: List:: } @@ -38,7 +38,7 @@ struct Contribution { } #[derive(Copy, Drop, Serde, starknet::Store)] -struct MontlyContribution { +struct MonthlyContribution { // @notice Contributor Address, used in update_contribution function contributor: ContractAddress, // @notice Contribution for dev guild @@ -55,16 +55,16 @@ struct MontlyContribution { } #[derive(Copy, Drop, Serde, starknet::Store)] -struct TotalMontlyContribution { - // @notice Montly contribution for dev guild +struct TotalMonthlyContribution { + // @notice Monthly contribution for dev guild dev: u32, - // @notice Montly contribution for design guild + // @notice Monthly contribution for design guild design: u32, - // @notice Montly contribution for problem solving guild + // @notice Monthly contribution for problem solving guild problem_solving: u32, - // @notice Montly contribution for marcom guild + // @notice Monthly contribution for marcom guild marcom: u32, - // @notice Montly contribution for research guild + // @notice Monthly contribution for research guild research: u32 } @@ -102,7 +102,7 @@ trait IMaster { // external functions - fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); + fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); fn initialise(ref self: TContractState, dev_guild: ContractAddress, design_guild: ContractAddress, marcom_guild: ContractAddress, problem_solver_guild: ContractAddress, research_guild: ContractAddress); fn migrate_points_initiated_by_DAO(ref self: TContractState, old_addresses: Array::, new_addresses: Array::); fn migrate_points_initiated_by_holder(ref self: TContractState, new_address: ContractAddress); @@ -128,7 +128,7 @@ mod Master { use integer::{u128_try_from_felt252, u256_sqrt, u256_from_felt252}; use starknet::syscalls::{replace_class_syscall, call_contract_syscall}; use alexandria_storage::list::{List, ListTrait}; - use super::{GuildPoints, Contribution, MontlyContribution, TotalMontlyContribution}; + use super::{GuildPoints, Contribution, MonthlyContribution, TotalMonthlyContribution}; use super::{ IGuildDispatcher, IGuildDispatcherTrait }; @@ -140,7 +140,7 @@ mod Master { #[storage] struct Storage { _contributions: LegacyMap::, // @dev contributions - _total_contribution: LegacyMap::, // @dev total contribution month wise [month_id => points] + _total_contribution: LegacyMap::, // @dev total contribution month wise [month_id => points] _last_update_id: u32, // @dev contribution update id _last_update_time: u64, // @dev timestamp for last update _dev_guild_SBT: ContractAddress, // @dev contract address for dev guild SBTs @@ -168,7 +168,7 @@ mod Master { update_id: u32, contributor: ContractAddress, month_id: u32, - points_earned: MontlyContribution + points_earned: MonthlyContribution } // @notice An event emitted whenever migration is queued @@ -288,7 +288,7 @@ mod Master { self._initialised.write(true); } - fn update_contibutions(ref self: ContractState, month_id: u32, contributions: Array::) { + fn update_contibutions(ref self: ContractState, month_id: u32, contributions: Array::) { self._only_owner(); let block_timestamp = get_block_timestamp(); let mut id = self._last_update_id.read(); @@ -399,7 +399,7 @@ mod Master { let updated_contribution = Contribution{dev: new_dev_contribution, design: new_design_contribution, problem_solving: new_problem_solving_contribution, marcom: new_marcom_contribution, research: new_research_contribution, last_timestamp: block_timestamp}; self._contributions.write(contributor, updated_contribution); - let total_monthy_contribution = TotalMontlyContribution{dev: dev_total_cum, design: design_total_cum, problem_solving: problem_solving_total_cum, marcom: marcom_total_cum, research: research_total_cum}; + let total_monthy_contribution = TotalMonthlyContribution{dev: dev_total_cum, design: design_total_cum, problem_solving: problem_solving_total_cum, marcom: marcom_total_cum, research: research_total_cum}; self._total_contribution.write(month_id, total_monthy_contribution); current_index += 1; // } From 0f59c827c998deaa109885eeb60c99cade71f1db Mon Sep 17 00:00:00 2001 From: yashm001 Date: Mon, 9 Oct 2023 17:17:50 +0530 Subject: [PATCH 04/22] fixes some bugs --- Scarb.toml | 1 + src/Master.cairo | 26 +++++++------- src/lib.cairo | 6 ++-- src/storage.cairo | 88 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 16 deletions(-) create mode 100644 src/storage.cairo diff --git a/Scarb.toml b/Scarb.toml index dc7f79a..b9a3f99 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -16,3 +16,4 @@ alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandri starknet = "2.2.0" snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.5.0" } alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git" } +openzeppelin = { git = "https://github.com/openzeppelin/cairo-contracts" } diff --git a/src/Master.cairo b/src/Master.cairo index 3799822..60ea524 100644 --- a/src/Master.cairo +++ b/src/Master.cairo @@ -86,11 +86,11 @@ trait IGuild { trait IMaster { // view functions fn get_contibutions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; - fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; - fn get_design_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; - fn get_problem_solving_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; - fn get_marcom_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; - fn get_research_points(self: @TContractState, contributor: ContractAddress) -> GuildPoints; + fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_design_points(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_problem_solving_points(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_marcom_points(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_research_points(self: @TContractState, contributor: ContractAddress) -> u32; fn get_last_update_id(self: @TContractState) -> u32; fn get_last_update_time(self: @TContractState) -> u64; fn get_migartion_queued_state(self: @TContractState, hash: felt252 ) -> bool; @@ -135,7 +135,7 @@ mod Master { // - // Storage Pair + // Storage Master // #[storage] struct Storage { @@ -158,8 +158,6 @@ mod Master { ContributionUpdated: ContributionUpdated, MigrationQueued: MigrationQueued, Migrated: Migrated, - // Swap: Swap, - // Sync: Sync } // @notice An event emitted whenever contribution is updated @@ -215,27 +213,27 @@ mod Master { fn get_dev_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { let contribution = self._contributions.read(contributor); - contribution.dev + contribution.dev.cum_score } fn get_design_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { let contribution = self._contributions.read(contributor); - contribution.design + contribution.design.cum_score } fn get_problem_solving_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { let contribution = self._contributions.read(contributor); - contribution.problem_solving + contribution.problem_solving.cum_score } fn get_marcom_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { let contribution = self._contributions.read(contributor); - contribution.marcom + contribution.marcom.cum_score } fn get_research_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { let contribution = self._contributions.read(contributor); - contribution.research + contribution.research.cum_score } fn get_last_update_id(self: @ContractState) -> u32 { @@ -471,7 +469,7 @@ mod Master { let research_guild = self._research_guild_SBT.read(); let contribution = self._contributions.read(old_address); - + /// @notice data needs to be empty list, TODO:: figure out how to create that. let zero_contribution = Contribution{dev: GuildPoints{ cum_score: 0_u32, data: List::}, design: GuildPoints{ cum_score: 0_u32, data: List::}, problem_solving: GuildPoints{ cum_score: 0_u32, data: List::}, diff --git a/src/lib.cairo b/src/lib.cairo index 81421e9..17e3108 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -1,2 +1,4 @@ -mod Master; -mod access; \ No newline at end of file +// mod Master; +mod access; +mod storage; +mod GuildSBT; \ No newline at end of file diff --git a/src/storage.cairo b/src/storage.cairo new file mode 100644 index 0000000..8729526 --- /dev/null +++ b/src/storage.cairo @@ -0,0 +1,88 @@ +use array::{ArrayTrait, SpanTrait}; +use traits::{Into, TryInto}; +use option::OptionTrait; +use starknet::{ + Store, storage_address_from_base_and_offset, storage_read_syscall, storage_write_syscall, + SyscallResult, StorageBaseAddress, +}; + +impl StoreSpanFelt252 of Store> { + fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult> { + StoreSpanFelt252::read_at_offset(address_domain, base, 0) + } + + fn write( + address_domain: u32, base: StorageBaseAddress, mut value: Span + ) -> SyscallResult<()> { + StoreSpanFelt252::write_at_offset(address_domain, base, 0, value) + } + + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult> { + let mut arr = array![]; + + // Read span len + let len: u8 = storage_read_syscall( + :address_domain, address: storage_address_from_base_and_offset(base, offset) + )? + .try_into() + .expect('Storage - Span too large'); + + // Load span content + let mut i: u8 = offset + 1; + loop { + if (i > len) { + break (); + } + + match storage_read_syscall( + :address_domain, address: storage_address_from_base_and_offset(base, i) + ) { + Result::Ok(element) => { + arr.append(element) + }, + Result::Err(_) => panic_with_felt252('Storage - Unknown error'), + } + + i += 1; + }; + + Result::Ok(arr.span()) + } + + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, mut value: Span + ) -> SyscallResult<()> { + // Assert span can fit in storage obj + // 1 slots for the len; 255 slots for the span content, minus the offset + let len: u8 = Into::::into(value.len() + offset.into()) + .try_into() + .expect('Storage - Span too large'); + + // Write span content + let mut i: u8 = offset + 1; + loop { + match value.pop_front() { + Option::Some(element) => { + storage_write_syscall( + :address_domain, + address: storage_address_from_base_and_offset(base, i), + value: *element + ); + i += 1; + }, + Option::None(_) => { + break (); + }, + }; + }; + + // Store span len + Store::::write(:address_domain, :base, value: len.into()) + } + + fn size() -> u8 { + 255 + } +} \ No newline at end of file From 86abc612cbe64d9f04dbfe6384399034642a5707 Mon Sep 17 00:00:00 2001 From: yashm001 Date: Mon, 9 Oct 2023 17:18:47 +0530 Subject: [PATCH 05/22] Update lib.cairo --- src/lib.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.cairo b/src/lib.cairo index 17e3108..c18b060 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -1,4 +1,4 @@ -// mod Master; +mod Master; mod access; mod storage; mod GuildSBT; \ No newline at end of file From 835a4b628aa17c87c023577b2e546d22578d316a Mon Sep 17 00:00:00 2001 From: yashm001 Date: Mon, 9 Oct 2023 17:55:15 +0530 Subject: [PATCH 06/22] added guildSBT --- src/GuildSBT.cairo | 337 +++++++++++++++++++++++++++++++++++++++++++++ src/array.cairo | 72 ++++++++++ src/lib.cairo | 3 +- 3 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 src/GuildSBT.cairo create mode 100644 src/array.cairo diff --git a/src/GuildSBT.cairo b/src/GuildSBT.cairo new file mode 100644 index 0000000..63629a4 --- /dev/null +++ b/src/GuildSBT.cairo @@ -0,0 +1,337 @@ +// @title Mesh Guild SBTs Cairo 2.2 +// @author Mesh Finance +// @license MIT +// @notice SBT contract to give out to contributor + +use starknet::ContractAddress; +use zeroable::Zeroable; +use array::{Array, ArrayTrait, SpanTrait}; +use serde::Serde; +use traits::{Into, TryInto}; + +#[starknet::interface] +trait IMaster { + fn get_dev_points(self: @T, contributor: ContractAddress) -> u32; +} + +// +// Contract Interface +// +#[starknet::interface] +trait IGuildSBT { + // view functions + fn tokenURI(self: @TContractState, token_id: u256) -> Span; + fn tokenURI_from_contributor(self: @TContractState, contributor: ContractAddress) -> Span; + fn get_master(self: @TContractState) -> ContractAddress; + fn get_contribution_tier(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_contribution_levels(self: @TContractState) -> Array; + fn get_number_of_levels(self: @TContractState) -> u32; + fn baseURI(self: @TContractState) -> Span; + + // external functions + fn update_baseURI(ref self: TContractState, new_baseURI: Span); + fn update_contribution_levels(ref self: TContractState, new_conribution_levels: Array); + fn update_master(ref self: TContractState, new_master: ContractAddress); + fn safe_mint(ref self: TContractState, token_type: u8); + fn migrate_sbt(ref self: TContractState, old_address: ContractAddress, new_address: ContractAddress); + +} + +#[starknet::contract] +mod GuildSBT { + + use option::OptionTrait; + use traits::{Into, TryInto, Default, Felt252DictValue}; + use array::{SpanSerde, ArrayTrait}; + use clone::Clone; + use array::SpanTrait; + use box::BoxTrait; + use ecdsa::check_ecdsa_signature; + use zeroable::Zeroable; + use openzeppelin::token::erc721::ERC721; + use openzeppelin::token::erc721::ERC721::InternalTrait as ERC721InternalTrait; + + use openzeppelin::introspection::interface::ISRC5; + use openzeppelin::introspection::interface::ISRC5Camel; + use openzeppelin::token::erc721::interface::{ + IERC721, IERC721CamelOnly, IERC721Metadata, IERC721MetadataCamelOnly + }; + use contributor_SBT2_0::access::ownable::{Ownable, IOwnable}; + use contributor_SBT2_0::access::ownable::Ownable::{ + ModifierTrait as OwnableModifierTrait, InternalTrait as OwnableInternalTrait, + }; + use starknet::ContractAddress; + use starknet::get_caller_address; + + // use alexandria_storage::list::{List, ListTrait}; + use contributor_SBT2_0::storage::StoreSpanFelt252; + use contributor_SBT2_0::array::StoreU32Array; + use super::{ + IMasterDispatcher, IMasterDispatcherTrait + }; + + const IERC721_ID_LEGACY: felt252 = 0x80ac58cd; + const IERC721_METADATA_ID_LEGACY: felt252 = 0x5b5e139f; + const IERC721_RECEIVER_ID_LEGACY: felt252 = 0x150b7a02; + + #[storage] + struct Storage { + _master: ContractAddress, + _contribution_levels: Array, + _baseURI: Span, + _token_type: LegacyMap::, + _next_token_id: u256, + _wallet_of_owner: LegacyMap::, + } + + // + // Constructor + // + + // @notice Contract constructor + #[constructor] + fn constructor(ref self: ContractState, name_: felt252, symbol_: felt252, baseURI_: Span, owner_: ContractAddress, master_: ContractAddress, contribution_levels_: Array) { + let mut erc721_self = ERC721::unsafe_new_contract_state(); + erc721_self.initializer(name: name_, symbol: symbol_); + + let mut ownable_self = Ownable::unsafe_new_contract_state(); + ownable_self._transfer_ownership(new_owner: owner_); + + self._baseURI.write(baseURI_); + self._master.write(master_); + self._next_token_id.write(1); + InternalImpl::_update_contribution_levels(ref self, contribution_levels_); + } + + #[external(v0)] + impl GuildSBT of super::IGuildSBT { + // + // Getters + // + fn tokenURI(self: @ContractState, token_id: u256) -> Span { + let erc721_self = ERC721::unsafe_new_contract_state(); + let owner = erc721_self.owner_of(:token_id); + let master = self._master.read(); + let masterDispatcher = IMasterDispatcher { contract_address: master }; + let points = masterDispatcher.get_dev_points(owner); + let token_type = self._token_type.read(owner); + + let tier = InternalImpl::_get_contribution_tier(self, points); + + InternalImpl::_get_tokenURI(self, tier, token_type) + + } + + + + fn tokenURI_from_contributor(self: @ContractState, contributor: ContractAddress) -> Span { + let master = self._master.read(); + let masterDispatcher = IMasterDispatcher { contract_address: master }; + let points = masterDispatcher.get_dev_points(contributor); + let token_type = self._token_type.read(contributor); + + let tier = InternalImpl::_get_contribution_tier(self, points); + + InternalImpl::_get_tokenURI(self, tier, token_type) + + } + + + fn get_master(self: @ContractState) -> ContractAddress { + self._master.read() + } + + + fn get_contribution_tier(self: @ContractState, contributor: ContractAddress) -> u32 { + let master = self._master.read(); + let masterDispatcher = IMasterDispatcher { contract_address: master }; + let points = masterDispatcher.get_dev_points(contributor); + InternalImpl::_get_contribution_tier(self, points) + } + + + fn get_contribution_levels(self: @ContractState) -> Array { + self._contribution_levels.read() + } + + fn get_number_of_levels(self: @ContractState) -> u32 { + self._contribution_levels.read().len() + } + + fn baseURI(self: @ContractState) -> Span { + self._baseURI.read() + } + + // + // Setters + // + + fn update_baseURI(ref self: ContractState, new_baseURI: Span) { + self._only_owner(); + self._baseURI.write(new_baseURI); + } + + fn update_contribution_levels(ref self: ContractState, new_conribution_levels: Array) { + self._only_owner(); + InternalImpl::_update_contribution_levels(ref self, new_conribution_levels); + + } + fn update_master(ref self: ContractState, new_master: ContractAddress) { + self._only_owner(); + self._master.write(new_master); + } + + fn safe_mint(ref self: ContractState, token_type: u8) { + let account = get_caller_address(); + let mut erc721_self = ERC721::unsafe_new_contract_state(); + + let balance = erc721_self.balance_of(:account); + assert (balance == 0, "ALREADY_MINTED"); + + self._token_type.write(account, token_type); + let master = self._master.read(); + let masterDispatcher = IMasterDispatcher { contract_address: master }; + let points = masterDispatcher.get_dev_points(account); + let tier = InternalImpl::_get_contribution_tier(@self, points); + + assert (tier != 0, "NOT_ENOUGH_POINTS"); + let token_id = self._next_token_id.read(); + erc721_self._mint(to: account, token_id: token_id.into()); + self._wallet_of_owner.write(account, token_id); + self._next_token_id.write(token_id + 1); + + } + + fn migrate_sbt(ref self: ContractState, old_address: ContractAddress, new_address: ContractAddress) { + self._only_master(); + let mut erc721_self = ERC721::unsafe_new_contract_state(); + + let old_address_balance = erc721_self.balance_of(account: old_address); + if (old_address_balance == 0) { + return (); + } + + let new_address_balance = erc721_self.balance_of(account: new_address); + assert (new_address_balance == 0, "SBT_ALREADY_FOUND"); + + let token_id = self._wallet_of_owner.read(old_address); + let token_type = self._token_type.read(old_address); + + erc721_self._transfer(from: old_address, to: new_address, :token_id); + + self._wallet_of_owner.write(old_address, 0); + self._wallet_of_owner.write(new_address, token_id); + self._token_type.write(new_address, token_type); + + } + + + + + + } + + #[generate_trait] + impl InternalImpl of InternalTrait { + + fn _update_contribution_levels(ref self: ContractState, new_contribution_levels: Array) { + self._contribution_levels.write(new_contribution_levels); + } + + fn _get_contribution_tier(self: @ContractState, points: u32) -> u32 { + let mut current_index = 0_u32; + let contribution_levels = self._contribution_levels.read(); + loop { + if (current_index == contribution_levels.len() - 1) { + break true; + } + + if (points < *contribution_levels[current_index]) { + break true; + } + + current_index += 1; + }; + current_index + } + + fn _get_tokenURI(self: @ContractState, tier: u32, token_type: u8) -> Span { + let baseURI = self._baseURI.read(); + let new_base_uri: Array = baseURI.snapshot.clone(); + let mut tmp: Array = InternalImpl::append_number_ascii(new_base_uri, tier.into()); + tmp = InternalImpl::append_number_ascii(tmp, token_type.into()); + tmp.append('.json'); + return tmp.span(); + } + + + + fn append_number_ascii(mut uri: Array, mut number_in: u256) -> Array { + // TODO: replace with u256 divide once it's implemented on network + let mut number: u128 = number_in.try_into().unwrap(); + let mut tmpArray: Array = ArrayTrait::new(); + loop { + if number == 0 { + break; + } + let digit: u128 = number % 10; + number /= 10; + tmpArray.append(digit.into() + 48); + }; + let mut i: u32 = tmpArray.len(); + if i == 0 { // deal with 0 case + uri.append(48); + } + loop { + if i == 0 { + break; + } + i -= 1; + uri.append(*tmpArray.get(i.into()).unwrap().unbox()); + }; + return uri; + } + } + + + + + #[generate_trait] + impl ModifierImpl of ModifierTrait { + fn _only_owner(self: @ContractState) { + let mut ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.assert_only_owner(); + } + + fn _only_master(self: @ContractState) { + let master = self._master.read(); + let caller = get_caller_address(); + assert(!caller.is_zero(), 'CALLER_IS_ZERO_ADDRESS'); + assert (caller == master, "UNAUTHORISED") + } + } + + #[external(v0)] + impl IOwnableImpl of IOwnable { + fn owner(self: @ContractState) -> ContractAddress { + let ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.owner() + } + + fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { + let mut ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.transfer_ownership(:new_owner); + } + + fn renounce_ownership(ref self: ContractState) { + let mut ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.renounce_ownership(); + } + } + +} + diff --git a/src/array.cairo b/src/array.cairo new file mode 100644 index 0000000..9ba6044 --- /dev/null +++ b/src/array.cairo @@ -0,0 +1,72 @@ +use array::{ArrayTrait, SpanTrait}; +use traits::{Into, TryInto}; +use option::OptionTrait; +use starknet::{ + Store, storage_address_from_base_and_offset, storage_read_syscall, storage_write_syscall, + SyscallResult, StorageBaseAddress, +}; + + +impl StoreU32Array of Store> { + fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult> { + StoreU32Array::read_at_offset(address_domain, base, 0) + } + + fn write( + address_domain: u32, base: StorageBaseAddress, value: Array + ) -> SyscallResult<()> { + StoreU32Array::write_at_offset(address_domain, base, 0, value) + } + + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, mut offset: u8 + ) -> SyscallResult> { + let mut arr: Array = ArrayTrait::new(); + + // Read the stored array's length. If the length is superior to 255, the read will fail. + let len: u8 = Store::::read_at_offset(address_domain, base, offset) + .expect('Storage Span too large'); + offset += 1; + + // Sequentially read all stored elements and append them to the array. + let exit = len + offset; + loop { + if offset >= exit { + break; + } + + let value = Store::::read_at_offset(address_domain, base, offset).unwrap(); + arr.append(value); + offset += Store::::size(); + }; + + // Return the array. + Result::Ok(arr) + } + + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, mut offset: u8, mut value: Array + ) -> SyscallResult<()> { + // // Store the length of the array in the first storage slot. + let len: u8 = value.len().try_into().expect('Storage - Span too large'); + Store::::write_at_offset(address_domain, base, offset, len); + offset += 1; + + // Store the array elements sequentially + loop { + match value.pop_front() { + Option::Some(element) => { + Store::::write_at_offset(address_domain, base, offset, element); + offset += Store::::size(); + }, + Option::None(_) => { + break Result::Ok(()); + } + }; + } + } + + fn size() -> u8 { + 255 * Store::::size() + } +} \ No newline at end of file diff --git a/src/lib.cairo b/src/lib.cairo index c18b060..e1952b8 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -1,4 +1,5 @@ mod Master; mod access; mod storage; -mod GuildSBT; \ No newline at end of file +mod GuildSBT; +mod array; \ No newline at end of file From 0d2b67c178962b1df92af63ba3eac86495e4f661 Mon Sep 17 00:00:00 2001 From: yashm001 Date: Mon, 9 Oct 2023 18:03:31 +0530 Subject: [PATCH 07/22] updated array storage --- src/Master.cairo | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Master.cairo b/src/Master.cairo index 60ea524..97fe05f 100644 --- a/src/Master.cairo +++ b/src/Master.cairo @@ -8,7 +8,7 @@ use zeroable::Zeroable; use array::{Array, ArrayTrait, SpanTrait}; use serde::Serde; use traits::{Into, TryInto}; -use alexandria_storage::list::{List, ListTrait}; +use contributor_SBT2_0::array::StoreU32Array; @@ -18,7 +18,7 @@ struct GuildPoints { cum_score: u32, // @notice Monthly points for contribution for eg [Sept_2023 -> 250] will be written as [092023, 250] // even index as month_id, immediate right is points in that month - data: List:: + data: Array } #[derive(Drop, Serde, starknet::Store)] @@ -127,7 +127,9 @@ mod Master { use starknet::{ContractAddress, ClassHash, SyscallResult, SyscallResultTrait, get_caller_address, get_contract_address, get_block_timestamp, contract_address_const}; use integer::{u128_try_from_felt252, u256_sqrt, u256_from_felt252}; use starknet::syscalls::{replace_class_syscall, call_contract_syscall}; - use alexandria_storage::list::{List, ListTrait}; + // use alexandria_storage::list::{List, ListTrait}; + use contributor_SBT2_0::array::StoreU32Array; + use super::{GuildPoints, Contribution, MonthlyContribution, TotalMonthlyContribution}; use super::{ IGuildDispatcher, IGuildDispatcherTrait @@ -211,27 +213,27 @@ mod Master { self._contributions.read(contributor) } - fn get_dev_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { + fn get_dev_points(self: @ContractState, contributor: ContractAddress) -> u32 { let contribution = self._contributions.read(contributor); contribution.dev.cum_score } - fn get_design_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { + fn get_design_points(self: @ContractState, contributor: ContractAddress) -> u32 { let contribution = self._contributions.read(contributor); contribution.design.cum_score } - fn get_problem_solving_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { + fn get_problem_solving_points(self: @ContractState, contributor: ContractAddress) -> u32 { let contribution = self._contributions.read(contributor); contribution.problem_solving.cum_score } - fn get_marcom_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { + fn get_marcom_points(self: @ContractState, contributor: ContractAddress) -> u32 { let contribution = self._contributions.read(contributor); contribution.marcom.cum_score } - fn get_research_points(self: @ContractState, contributor: ContractAddress) -> GuildPoints { + fn get_research_points(self: @ContractState, contributor: ContractAddress) -> u32 { let contribution = self._contributions.read(contributor); contribution.research.cum_score } @@ -470,11 +472,11 @@ mod Master { let contribution = self._contributions.read(old_address); /// @notice data needs to be empty list, TODO:: figure out how to create that. - let zero_contribution = Contribution{dev: GuildPoints{ cum_score: 0_u32, data: List::}, - design: GuildPoints{ cum_score: 0_u32, data: List::}, - problem_solving: GuildPoints{ cum_score: 0_u32, data: List::}, - marcom: GuildPoints{ cum_score: 0_u32, data: List::}, - research: GuildPoints{ cum_score: 0_u32, data: List::}, + let zero_contribution = Contribution{dev: GuildPoints{ cum_score: 0_u32, data: ArrayTrait::new()}, + design: GuildPoints{ cum_score: 0_u32, data: ArrayTrait::new()}, + problem_solving: GuildPoints{ cum_score: 0_u32, data: ArrayTrait::new()}, + marcom: GuildPoints{ cum_score: 0_u32, data:ArrayTrait::new()}, + research: GuildPoints{ cum_score: 0_u32, data: ArrayTrait::new()}, last_timestamp: 0_u64 }; From 3fc84ead5a7210a83e37f52c9105ccbee979d255 Mon Sep 17 00:00:00 2001 From: yashm001 Date: Wed, 11 Oct 2023 15:17:55 +0530 Subject: [PATCH 08/22] replace " with ' in error message --- .gitignore | 105 +-------------------------------------------- Scarb.toml | 5 +-- src/GuildSBT.cairo | 8 ++-- src/Master.cairo | 8 ++-- 4 files changed, 11 insertions(+), 115 deletions(-) diff --git a/.gitignore b/.gitignore index 6704566..1de5659 100644 --- a/.gitignore +++ b/.gitignore @@ -1,104 +1 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port +target \ No newline at end of file diff --git a/Scarb.toml b/Scarb.toml index b9a3f99..7999662 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -13,7 +13,6 @@ allowed-libfuncs-list.name = "all" alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git" } [dependencies] -starknet = "2.2.0" +starknet = ">=2.2.0" snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.5.0" } -alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git" } -openzeppelin = { git = "https://github.com/openzeppelin/cairo-contracts" } +openzeppelin = { git = "https://github.com/openzeppelin/cairo-contracts", tag = "v0.7.0" } diff --git a/src/GuildSBT.cairo b/src/GuildSBT.cairo index 63629a4..fb05318 100644 --- a/src/GuildSBT.cairo +++ b/src/GuildSBT.cairo @@ -186,7 +186,7 @@ mod GuildSBT { let mut erc721_self = ERC721::unsafe_new_contract_state(); let balance = erc721_self.balance_of(:account); - assert (balance == 0, "ALREADY_MINTED"); + assert (balance == 0, 'ALREADY_MINTED'); self._token_type.write(account, token_type); let master = self._master.read(); @@ -194,7 +194,7 @@ mod GuildSBT { let points = masterDispatcher.get_dev_points(account); let tier = InternalImpl::_get_contribution_tier(@self, points); - assert (tier != 0, "NOT_ENOUGH_POINTS"); + assert (tier != 0, 'NOT_ENOUGH_POINTS'); let token_id = self._next_token_id.read(); erc721_self._mint(to: account, token_id: token_id.into()); self._wallet_of_owner.write(account, token_id); @@ -212,7 +212,7 @@ mod GuildSBT { } let new_address_balance = erc721_self.balance_of(account: new_address); - assert (new_address_balance == 0, "SBT_ALREADY_FOUND"); + assert (new_address_balance == 0, 'SBT_ALREADY_FOUND'); let token_id = self._wallet_of_owner.read(old_address); let token_type = self._token_type.read(old_address); @@ -308,7 +308,7 @@ mod GuildSBT { let master = self._master.read(); let caller = get_caller_address(); assert(!caller.is_zero(), 'CALLER_IS_ZERO_ADDRESS'); - assert (caller == master, "UNAUTHORISED") + assert (caller == master, 'UNAUTHORISED') } } diff --git a/src/Master.cairo b/src/Master.cairo index 97fe05f..071d37a 100644 --- a/src/Master.cairo +++ b/src/Master.cairo @@ -278,7 +278,7 @@ mod Master { fn initialise(ref self: ContractState, dev_guild: ContractAddress, design_guild: ContractAddress, marcom_guild: ContractAddress, problem_solver_guild: ContractAddress, research_guild: ContractAddress) { self._only_owner(); let is_initialised = self._initialised.read(); - assert (is_initialised == false, "ALREADY_INITIALISED"); + assert (is_initialised == false, 'ALREADY_INITIALISED'); self._dev_guild_SBT.write(dev_guild); self._design_guild_SBT.write(design_guild); @@ -415,7 +415,7 @@ mod Master { fn migrate_points_initiated_by_DAO(ref self: ContractState, old_addresses: Array::, new_addresses: Array:: ) { self._only_owner(); - assert(old_addresses.len() == new_addresses.len(), "INVALID_INPUTS"); + assert(old_addresses.len() == new_addresses.len(), 'INVALID_INPUTS'); let mut current_index = 0; loop { @@ -444,11 +444,11 @@ mod Master { let migration_hash: felt252 = LegacyHash::hash(old_address.into(), new_address); let is_queued = self._queued_migrations.read(migration_hash); - assert(is_queued == true, "NOT_QUEUED"); + assert(is_queued == true, 'NOT_QUEUED'); self._queued_migrations.write(migration_hash, false); InternalImpl::_migrate_points(ref self, old_address, new_address); - + // self._last_update_id.write(0); } From 4e0334b180c99e36b1ff7801ef8dc6dc0a035bef Mon Sep 17 00:00:00 2001 From: yashm001 Date: Wed, 11 Oct 2023 16:12:23 +0530 Subject: [PATCH 09/22] added erc721 impl --- src/GuildSBT.cairo | 148 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/src/GuildSBT.cairo b/src/GuildSBT.cairo index fb05318..23ba926 100644 --- a/src/GuildSBT.cairo +++ b/src/GuildSBT.cairo @@ -294,6 +294,154 @@ mod GuildSBT { } + // + // ERC721 ABI impl + // + + #[external(v0)] + impl IERC721Impl of IERC721 { + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { + let erc721_self = ERC721::unsafe_new_contract_state(); + + erc721_self.balance_of(:account) + } + + fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { + let erc721_self = ERC721::unsafe_new_contract_state(); + + erc721_self.owner_of(:token_id) + } + + fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { + let erc721_self = ERC721::unsafe_new_contract_state(); + + erc721_self.get_approved(:token_id) + } + + fn is_approved_for_all( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + let erc721_self = ERC721::unsafe_new_contract_state(); + + erc721_self.is_approved_for_all(:owner, :operator) + } + + fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { + let mut erc721_self = ERC721::unsafe_new_contract_state(); + + erc721_self.approve(:to, :token_id); + } + + fn transfer_from( + ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ) { + let mut erc721_self = ERC721::unsafe_new_contract_state(); + + erc721_self.transfer_from(:from, :to, :token_id); + } + + fn safe_transfer_from( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ) { + let mut erc721_self = ERC721::unsafe_new_contract_state(); + + erc721_self.safe_transfer_from(:from, :to, :token_id, :data); + } + + fn set_approval_for_all( + ref self: ContractState, operator: ContractAddress, approved: bool + ) { + let mut erc721_self = ERC721::unsafe_new_contract_state(); + + erc721_self.set_approval_for_all(:operator, :approved); + } + } + + #[external(v0)] + impl ISRC5Impl of ISRC5 { + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + if ((interface_id == IERC721_ID_LEGACY) + | (interface_id == IERC721_METADATA_ID_LEGACY)) { + true + } else { + let mut erc721_self = ERC721::unsafe_new_contract_state(); + erc721_self.supports_interface(:interface_id) + } + } + } + + #[external(v0)] + impl ISRC5CamelImpl of ISRC5Camel { + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + self.supports_interface(interface_id: interfaceId) + } + } + + #[external(v0)] + impl IERC721MetadataImpl of IERC721Metadata { + fn name(self: @ContractState) -> felt252 { + let erc721_self = ERC721::unsafe_new_contract_state(); + + erc721_self.name() + } + + fn symbol(self: @ContractState) -> felt252 { + let erc721_self = ERC721::unsafe_new_contract_state(); + + erc721_self.symbol() + } + + fn token_uri(self: @ContractState, token_id: u256) -> felt252 { + let erc721_self = ERC721::unsafe_new_contract_state(); + + erc721_self.token_uri(:token_id) + } + } + + #[external(v0)] + impl JediERC721CamelImpl of IERC721CamelOnly { + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { + IERC721::balance_of(self, account: account) + } + + fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress { + IERC721::owner_of(self, token_id: tokenId) + } + + fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { + IERC721::get_approved(self, token_id: tokenId) + } + + fn isApprovedForAll( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + IERC721::is_approved_for_all(self, owner: owner, operator: operator) + } + + fn transferFrom( + ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 + ) { + IERC721::transfer_from(ref self, :from, :to, token_id: tokenId); + } + + fn safeTransferFrom( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span + ) { + IERC721::safe_transfer_from(ref self, :from, :to, token_id: tokenId, :data); + } + + fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { + IERC721::set_approval_for_all(ref self, :operator, :approved); + } + } #[generate_trait] From 6c2317c86981fb563772ebc7392d07cb443772ba Mon Sep 17 00:00:00 2001 From: yashm001 Date: Thu, 12 Oct 2023 00:48:02 +0530 Subject: [PATCH 10/22] Update GuildSBT.cairo --- src/GuildSBT.cairo | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/GuildSBT.cairo b/src/GuildSBT.cairo index 23ba926..4e1c015 100644 --- a/src/GuildSBT.cairo +++ b/src/GuildSBT.cairo @@ -27,6 +27,7 @@ trait IGuildSBT { fn get_contribution_levels(self: @TContractState) -> Array; fn get_number_of_levels(self: @TContractState) -> u32; fn baseURI(self: @TContractState) -> Span; + fn wallet_of_owner(self: @TContractState, account: ContractAddress) -> u256; // external functions fn update_baseURI(ref self: TContractState, new_baseURI: Span); @@ -162,6 +163,11 @@ mod GuildSBT { self._baseURI.read() } + fn wallet_of_owner(self: @ContractState, account: ContractAddress) -> u256 { + self._wallet_of_owner.read(account) + } + + // // Setters // From fb37fa8539651af0c9df0121e9a1cc75e25394ea Mon Sep 17 00:00:00 2001 From: yashm001 Date: Thu, 12 Oct 2023 00:49:19 +0530 Subject: [PATCH 11/22] updated master design --- src/Master.cairo | 159 ++++++----- src/Master_approach1.cairo | 541 +++++++++++++++++++++++++++++++++++++ 2 files changed, 620 insertions(+), 80 deletions(-) create mode 100644 src/Master_approach1.cairo diff --git a/src/Master.cairo b/src/Master.cairo index 071d37a..7263ab5 100644 --- a/src/Master.cairo +++ b/src/Master.cairo @@ -12,27 +12,27 @@ use contributor_SBT2_0::array::StoreU32Array; -#[derive(Drop, Serde, starknet::Store)] -struct GuildPoints { - // @notice the cummulative score for each contributor - cum_score: u32, - // @notice Monthly points for contribution for eg [Sept_2023 -> 250] will be written as [092023, 250] - // even index as month_id, immediate right is points in that month - data: Array -} +// #[derive(Drop, Serde, starknet::Store)] +// struct GuildPoints { +// // @notice the cummulative score for each contributor +// cum_score: u32, +// // @notice Monthly points for contribution for eg [Sept_2023 -> 250] will be written as [092023, 250] +// // even index as month_id, immediate right is points in that month +// data: Array +// } #[derive(Drop, Serde, starknet::Store)] struct Contribution { // @notice Contribution for dev guild - dev: GuildPoints, + dev: u32,//GuildPoints, // @notice Contribution for design guild - design: GuildPoints, + design: u32,//GuildPoints, // @notice Contribution for problem solving guild - problem_solving: GuildPoints, + problem_solving: u32,//GuildPoints, // @notice Contribution for marcom guild - marcom: GuildPoints, + marcom: u32,//GuildPoints, // @notice Contribution for research guild - research: GuildPoints, + research: u32,//GuildPoints, // @notice timestamp for the last update last_timestamp: u64 } @@ -99,7 +99,8 @@ trait IMaster { fn get_marcom_guild_SBT(self: @TContractState) -> ContractAddress; fn get_problem_solving_guild_SBT(self: @TContractState) -> ContractAddress; fn get_research_guild_SBT(self: @TContractState) -> ContractAddress; - + fn get_total_contribution(self: @TContractState, month_id: u32) -> TotalMonthlyContribution; + fn get_contributions_data(self: @TContractState, contributor: ContractAddress, guild: felt252) -> Array; // external functions fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); @@ -130,7 +131,7 @@ mod Master { // use alexandria_storage::list::{List, ListTrait}; use contributor_SBT2_0::array::StoreU32Array; - use super::{GuildPoints, Contribution, MonthlyContribution, TotalMonthlyContribution}; + use super::{Contribution, MonthlyContribution, TotalMonthlyContribution}; use super::{ IGuildDispatcher, IGuildDispatcherTrait }; @@ -141,7 +142,8 @@ mod Master { // #[storage] struct Storage { - _contributions: LegacyMap::, // @dev contributions + _contributions: LegacyMap::, // @dev contributions + _contributions_data: LegacyMap::<(ContractAddress, felt252), Array>, // @dev contributions _total_contribution: LegacyMap::, // @dev total contribution month wise [month_id => points] _last_update_id: u32, // @dev contribution update id _last_update_time: u64, // @dev timestamp for last update @@ -213,29 +215,37 @@ mod Master { self._contributions.read(contributor) } + fn get_total_contribution(self: @ContractState, month_id: u32) -> TotalMonthlyContribution { + self._total_contribution.read(month_id) + } + + fn get_contributions_data(self: @ContractState, contributor: ContractAddress, guild: felt252) -> Array { + self._contributions_data.read((contributor, guild)) + } + fn get_dev_points(self: @ContractState, contributor: ContractAddress) -> u32 { - let contribution = self._contributions.read(contributor); - contribution.dev.cum_score + let contribution: Contribution = self._contributions.read(contributor); + contribution.dev } fn get_design_points(self: @ContractState, contributor: ContractAddress) -> u32 { let contribution = self._contributions.read(contributor); - contribution.design.cum_score + contribution.design } fn get_problem_solving_points(self: @ContractState, contributor: ContractAddress) -> u32 { let contribution = self._contributions.read(contributor); - contribution.problem_solving.cum_score + contribution.problem_solving } fn get_marcom_points(self: @ContractState, contributor: ContractAddress) -> u32 { let contribution = self._contributions.read(contributor); - contribution.marcom.cum_score + contribution.marcom } fn get_research_points(self: @ContractState, contributor: ContractAddress) -> u32 { let contribution = self._contributions.read(contributor); - contribution.research.cum_score + contribution.research } fn get_last_update_id(self: @ContractState) -> u32 { @@ -305,95 +315,67 @@ mod Master { if (current_index == contributions.len() - 1) { break true; } - let new_contributions = *contributions[current_index]; + let new_contributions: MonthlyContribution = *contributions[current_index]; let contributor: ContractAddress = new_contributions.contributor; - // let points = new_contributions.points; - // let mut points_index = 0; - - // loop { - // if (points_index == 4) { - // break true; - // } - // let contribution_point = *points[points_index]; - // if (contribution_point == 0) { - // continue true; - // } - // } - let old_contribution = self._contributions.read(contributor); - let old_dev_contribution = old_contribution.dev; - let mut contribution_data_dev = old_dev_contribution.data; - let mut cum_score_dev = old_dev_contribution.cum_score; if (new_contributions.dev != 0) { - + let mut contribution_data_dev = self._contributions_data.read((contributor,'dev')); + contribution_data_dev.append(month_id); contribution_data_dev.append(new_contributions.dev); - cum_score_dev = cum_score_dev + new_contributions.dev; - - dev_total_cum = dev_total_cum + new_contributions.dev; + self._contributions_data.write((contributor,'dev'),contribution_data_dev); } - let new_dev_contribution = GuildPoints{cum_score: cum_score_dev, data: contribution_data_dev}; + let new_dev_contribution = old_contribution.dev + new_contributions.dev; + - let old_design_contribution = old_contribution.design; - let mut contribution_data_design = old_design_contribution.data; - let mut cum_score_design = old_design_contribution.cum_score; if (new_contributions.design != 0) { - + let mut contribution_data_design = self._contributions_data.read((contributor,'design')); + contribution_data_design.append(month_id); contribution_data_design.append(new_contributions.design); - cum_score_design = cum_score_design + new_contributions.design; + self._contributions_data.write((contributor,'design'),contribution_data_design); - design_total_cum = design_total_cum + new_contributions.design; } - let new_design_contribution = GuildPoints{cum_score: cum_score_design, data: contribution_data_design}; + let new_design_contribution = old_contribution.design + new_contributions.design; - let old_problem_solving_contribution = old_contribution.problem_solving; - let mut contribution_data_problem_solving = old_problem_solving_contribution.data; - let mut cum_score_problem_solving = old_problem_solving_contribution.cum_score; if (new_contributions.problem_solving != 0) { - + let mut contribution_data_problem_solving = self._contributions_data.read((contributor,'problem_solving')); + contribution_data_problem_solving.append(month_id); contribution_data_problem_solving.append(new_contributions.problem_solving); - cum_score_problem_solving = cum_score_problem_solving + new_contributions.problem_solving; + self._contributions_data.write((contributor,'problem_solving'),contribution_data_problem_solving); - problem_solving_total_cum = problem_solving_total_cum + new_contributions.problem_solving; } - let new_problem_solving_contribution = GuildPoints{cum_score: cum_score_problem_solving, data: contribution_data_problem_solving}; + let new_problem_solving_contribution = old_contribution.problem_solving + new_contributions.problem_solving; - let old_marcom_contribution = old_contribution.marcom; - let mut contribution_data_marcom = old_marcom_contribution.data; - let mut cum_score_marcom = old_marcom_contribution.cum_score; if (new_contributions.marcom != 0) { - + let mut contribution_data_marcom = self._contributions_data.read((contributor,'marcom')); + contribution_data_marcom.append(month_id); contribution_data_marcom.append(new_contributions.marcom); - cum_score_marcom = cum_score_marcom + new_contributions.marcom; - marcom_total_cum = marcom_total_cum + new_contributions.marcom; + self._contributions_data.write((contributor,'marcom'),contribution_data_marcom); } - let new_marcom_contribution = GuildPoints{cum_score: cum_score_marcom, data: contribution_data_marcom}; + let new_marcom_contribution = old_contribution.marcom + new_contributions.marcom; - let old_research_contribution = old_contribution.research; - let mut contribution_data_research = old_research_contribution.data; - let mut cum_score_research = old_research_contribution.cum_score; if (new_contributions.research != 0) { - + let mut contribution_data_research = self._contributions_data.read((contributor,'research')); + contribution_data_research.append(month_id); contribution_data_research.append(new_contributions.research); - cum_score_research = cum_score_research + new_contributions.research; + self._contributions_data.write((contributor,'research'),contribution_data_research); - research_total_cum = research_total_cum + new_contributions.research; } - let new_research_contribution = GuildPoints{cum_score: cum_score_research, data: contribution_data_research}; + let new_research_contribution = old_contribution.research + new_contributions.research; let updated_contribution = Contribution{dev: new_dev_contribution, design: new_design_contribution, problem_solving: new_problem_solving_contribution, marcom: new_marcom_contribution, research: new_research_contribution, last_timestamp: block_timestamp}; @@ -402,7 +384,7 @@ mod Master { let total_monthy_contribution = TotalMonthlyContribution{dev: dev_total_cum, design: design_total_cum, problem_solving: problem_solving_total_cum, marcom: marcom_total_cum, research: research_total_cum}; self._total_contribution.write(month_id, total_monthy_contribution); current_index += 1; - // } + self.emit(ContributionUpdated{update_id: id, contributor: contributor, month_id: month_id, points_earned: new_contributions}); }; @@ -448,7 +430,6 @@ mod Master { self._queued_migrations.write(migration_hash, false); InternalImpl::_migrate_points(ref self, old_address, new_address); - // self._last_update_id.write(0); } @@ -471,18 +452,36 @@ mod Master { let research_guild = self._research_guild_SBT.read(); let contribution = self._contributions.read(old_address); - /// @notice data needs to be empty list, TODO:: figure out how to create that. - let zero_contribution = Contribution{dev: GuildPoints{ cum_score: 0_u32, data: ArrayTrait::new()}, - design: GuildPoints{ cum_score: 0_u32, data: ArrayTrait::new()}, - problem_solving: GuildPoints{ cum_score: 0_u32, data: ArrayTrait::new()}, - marcom: GuildPoints{ cum_score: 0_u32, data:ArrayTrait::new()}, - research: GuildPoints{ cum_score: 0_u32, data: ArrayTrait::new()}, + let zero_contribution = Contribution{dev: 0_u32, + design: 0_u32, + problem_solving: 0_u32, + marcom: 0_u32, + research: 0_u32, last_timestamp: 0_u64 }; self._contributions.write(old_address, zero_contribution); self._contributions.write(new_address, contribution); + // updating contribution data + let dev_data = self._contributions_data.read((old_address, 'dev')); + let design_data = self._contributions_data.read((old_address, 'design')); + let problem_solving_data = self._contributions_data.read((old_address, 'problem_solving')); + let marcom_data = self._contributions_data.read((old_address, 'marcom')); + let research_data = self._contributions_data.read((old_address, 'research')); + + self._contributions_data.write((new_address, 'dev'), dev_data); + self._contributions_data.write((new_address, 'design'), design_data); + self._contributions_data.write((new_address, 'problem_solving'), problem_solving_data); + self._contributions_data.write((new_address, 'marcom'), marcom_data); + self._contributions_data.write((new_address, 'research'), research_data); + + self._contributions_data.write((old_address, 'dev'), ArrayTrait::new()); + self._contributions_data.write((old_address, 'design'), ArrayTrait::new()); + self._contributions_data.write((old_address, 'problem_solving'), ArrayTrait::new()); + self._contributions_data.write((old_address, 'marcom'), ArrayTrait::new()); + self._contributions_data.write((old_address, 'research'), ArrayTrait::new()); + let dev_guildDispatcher = IGuildDispatcher { contract_address: dev_guild }; dev_guildDispatcher.migrate_sbt(old_address, new_address); diff --git a/src/Master_approach1.cairo b/src/Master_approach1.cairo new file mode 100644 index 0000000..3a21f2e --- /dev/null +++ b/src/Master_approach1.cairo @@ -0,0 +1,541 @@ +// @title Mesh Contributor SBTs Master Cairo 2.2 +// @author Mesh Finance +// @license MIT +// @notice Master to store contribution points + +use starknet::ContractAddress; +use zeroable::Zeroable; +use array::{Array, ArrayTrait, SpanTrait}; +use serde::Serde; +use traits::{Into, TryInto}; +use contributor_SBT2_0::array::StoreU32Array; + + + +#[derive(Drop, Serde, starknet::Store)] +struct GuildPoints { + // @notice the cummulative score for each contributor + cum_score: u32, + // @notice Monthly points for contribution for eg [Sept_2023 -> 250] will be written as [092023, 250] + // even index as month_id, immediate right is points in that month + data: Array +} + +#[derive(Drop, Serde, starknet::Store)] +struct Contribution { + // @notice Contribution for dev guild + dev: GuildPoints, + // @notice Contribution for design guild + design: GuildPoints, + // @notice Contribution for problem solving guild + problem_solving: GuildPoints, + // @notice Contribution for marcom guild + marcom: GuildPoints, + // @notice Contribution for research guild + research: GuildPoints, + // @notice timestamp for the last update + last_timestamp: u64 +} + +#[derive(Copy, Drop, Serde, starknet::Store)] +struct MonthlyContribution { + // @notice Contributor Address, used in update_contribution function + contributor: ContractAddress, + // @notice Contribution for dev guild + dev: u32, + // @notice Contribution for design guild + design: u32, + // @notice Contribution for problem solving guild + problem_solving: u32, + // @notice Contribution for marcom guild + marcom: u32, + // @notice Contribution for research guild + research: u32 + +} + +#[derive(Copy, Drop, Serde, starknet::Store)] +struct TotalMonthlyContribution { + // @notice Monthly contribution for dev guild + dev: u32, + // @notice Monthly contribution for design guild + design: u32, + // @notice Monthly contribution for problem solving guild + problem_solving: u32, + // @notice Monthly contribution for marcom guild + marcom: u32, + // @notice Monthly contribution for research guild + research: u32 +} + +// +// External Interfaces +// + + +#[starknet::interface] +trait IGuild { + fn migrate_sbt(ref self: T, old_address: ContractAddress, new_address: ContractAddress); +} + + +// +// Contract Interface +// +#[starknet::interface] +trait IMaster { + // view functions + fn get_contibutions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; + fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_design_points(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_problem_solving_points(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_marcom_points(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_research_points(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_last_update_id(self: @TContractState) -> u32; + fn get_last_update_time(self: @TContractState) -> u64; + fn get_migartion_queued_state(self: @TContractState, hash: felt252 ) -> bool; + fn get_dev_guild_SBT(self: @TContractState) -> ContractAddress; + fn get_design_guild_SBT(self: @TContractState) -> ContractAddress; + fn get_marcom_guild_SBT(self: @TContractState) -> ContractAddress; + fn get_problem_solving_guild_SBT(self: @TContractState) -> ContractAddress; + fn get_research_guild_SBT(self: @TContractState) -> ContractAddress; + + + // external functions + fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); + fn initialise(ref self: TContractState, dev_guild: ContractAddress, design_guild: ContractAddress, marcom_guild: ContractAddress, problem_solver_guild: ContractAddress, research_guild: ContractAddress); + fn migrate_points_initiated_by_DAO(ref self: TContractState, old_addresses: Array::, new_addresses: Array::); + fn migrate_points_initiated_by_holder(ref self: TContractState, new_address: ContractAddress); + fn execute_migrate_points_initiated_by_holder(ref self: TContractState, old_address: ContractAddress, new_address: ContractAddress); + + +} + + +#[starknet::contract] +mod Master { + use traits::Into; // TODO remove intos when u256 inferred type is available + use option::OptionTrait; + use array::{ArrayTrait, SpanTrait}; + use result::ResultTrait; + use zeroable::Zeroable; + use hash::LegacyHash; + use contributor_SBT2_0::access::ownable::{Ownable, IOwnable}; + use contributor_SBT2_0::access::ownable::Ownable::{ + ModifierTrait as OwnableModifierTrait, InternalTrait as OwnableInternalTrait, + }; + use starknet::{ContractAddress, ClassHash, SyscallResult, SyscallResultTrait, get_caller_address, get_contract_address, get_block_timestamp, contract_address_const}; + use integer::{u128_try_from_felt252, u256_sqrt, u256_from_felt252}; + use starknet::syscalls::{replace_class_syscall, call_contract_syscall}; + // use alexandria_storage::list::{List, ListTrait}; + use contributor_SBT2_0::array::StoreU32Array; + + use super::{GuildPoints, Contribution, MonthlyContribution, TotalMonthlyContribution}; + use super::{ + IGuildDispatcher, IGuildDispatcherTrait + }; + + + // + // Storage Master + // + #[storage] + struct Storage { + _contributions: LegacyMap::, // @dev contributions + _total_contribution: LegacyMap::, // @dev total contribution month wise [month_id => points] + _last_update_id: u32, // @dev contribution update id + _last_update_time: u64, // @dev timestamp for last update + _dev_guild_SBT: ContractAddress, // @dev contract address for dev guild SBTs + _design_guild_SBT: ContractAddress, // @dev contract address for design guild guild SBTs + _marcom_guild_SBT: ContractAddress, // @dev contract address for marcom guild SBTs + _problem_solving_guild_SBT: ContractAddress, // @dev contract address for problem solving guild SBTs + _research_guild_SBT: ContractAddress, // @dev contract address for research guild SBTs + _initialised: bool, // @dev Flag to store initialisation state + _queued_migrations: LegacyMap::, // @dev flag to store queued migration requests. + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + ContributionUpdated: ContributionUpdated, + MigrationQueued: MigrationQueued, + Migrated: Migrated, + } + + // @notice An event emitted whenever contribution is updated + #[derive(Drop, starknet::Event)] + struct ContributionUpdated { + update_id: u32, + contributor: ContractAddress, + month_id: u32, + points_earned: MonthlyContribution + } + + // @notice An event emitted whenever migration is queued + #[derive(Drop, starknet::Event)] + struct MigrationQueued { + old_address: ContractAddress, + new_address: ContractAddress, + hash: felt252 + } + + // @notice An event emitted whenever SBT is migrated + #[derive(Drop, starknet::Event)] + struct Migrated { + old_address: ContractAddress, + new_address: ContractAddress + } + + + // + // Constructor + // + + // @notice Contract constructor + #[constructor] + fn constructor(ref self: ContractState, owner_: ContractAddress,) { + // @notice not sure if default is already zero or need to initialise. + self._last_update_id.write(0_u32); + self._last_update_time.write(0_u64); + self._initialised.write(false); + + let mut ownable_self = Ownable::unsafe_new_contract_state(); + ownable_self._transfer_ownership(new_owner: owner_); + + } + + #[external(v0)] + impl Master of super::IMaster { + // + // Getters + // + fn get_contibutions_points(self: @ContractState, contributor: ContractAddress) -> Contribution { + self._contributions.read(contributor) + } + + fn get_dev_points(self: @ContractState, contributor: ContractAddress) -> u32 { + let contribution = self._contributions.read(contributor); + contribution.dev.cum_score + } + + fn get_design_points(self: @ContractState, contributor: ContractAddress) -> u32 { + let contribution = self._contributions.read(contributor); + contribution.design.cum_score + } + + fn get_problem_solving_points(self: @ContractState, contributor: ContractAddress) -> u32 { + let contribution = self._contributions.read(contributor); + contribution.problem_solving.cum_score + } + + fn get_marcom_points(self: @ContractState, contributor: ContractAddress) -> u32 { + let contribution = self._contributions.read(contributor); + contribution.marcom.cum_score + } + + fn get_research_points(self: @ContractState, contributor: ContractAddress) -> u32 { + let contribution = self._contributions.read(contributor); + contribution.research.cum_score + } + + fn get_last_update_id(self: @ContractState) -> u32 { + self._last_update_id.read() + } + + fn get_last_update_time(self: @ContractState) -> u64 { + self._last_update_time.read() + } + + fn get_migartion_queued_state(self: @ContractState, hash: felt252 ) -> bool { + self._queued_migrations.read(hash) + } + + fn get_dev_guild_SBT(self: @ContractState) -> ContractAddress { + self._dev_guild_SBT.read() + } + + fn get_design_guild_SBT(self: @ContractState) -> ContractAddress { + self._design_guild_SBT.read() + } + + fn get_marcom_guild_SBT(self: @ContractState) -> ContractAddress { + self._marcom_guild_SBT.read() + } + + fn get_problem_solving_guild_SBT(self: @ContractState) -> ContractAddress { + self._problem_solving_guild_SBT.read() + } + + fn get_research_guild_SBT(self: @ContractState) -> ContractAddress { + self._research_guild_SBT.read() + } + + + // + // Setters + // + + fn initialise(ref self: ContractState, dev_guild: ContractAddress, design_guild: ContractAddress, marcom_guild: ContractAddress, problem_solver_guild: ContractAddress, research_guild: ContractAddress) { + self._only_owner(); + let is_initialised = self._initialised.read(); + assert (is_initialised == false, 'ALREADY_INITIALISED'); + + self._dev_guild_SBT.write(dev_guild); + self._design_guild_SBT.write(design_guild); + self._marcom_guild_SBT.write(marcom_guild); + self._problem_solving_guild_SBT.write(problem_solver_guild); + self._research_guild_SBT.write(research_guild); + self._initialised.write(true); + } + + fn update_contibutions(ref self: ContractState, month_id: u32, contributions: Array::) { + self._only_owner(); + let block_timestamp = get_block_timestamp(); + let mut id = self._last_update_id.read(); + let mut current_index = 0; + + // for keeping track of cummulative guild points for that month. + let mut dev_total_cum = 0_u32; + let mut design_total_cum = 0_u32; + let mut problem_solving_total_cum = 0_u32; + let mut marcom_total_cum = 0_u32; + let mut research_total_cum = 0_u32; + + loop { + if (current_index == contributions.len() - 1) { + break true; + } + let new_contributions = *contributions[current_index]; + let contributor: ContractAddress = new_contributions.contributor; + // let points = new_contributions.points; + // let mut points_index = 0; + + // loop { + // if (points_index == 4) { + // break true; + // } + // let contribution_point = *points[points_index]; + // if (contribution_point == 0) { + // continue true; + // } + // } + + let old_contribution = self._contributions.read(contributor); + + let old_dev_contribution = old_contribution.dev; + let mut contribution_data_dev = old_dev_contribution.data; + let mut cum_score_dev = old_dev_contribution.cum_score; + if (new_contributions.dev != 0) { + + contribution_data_dev.append(month_id); + contribution_data_dev.append(new_contributions.dev); + + cum_score_dev = cum_score_dev + new_contributions.dev; + + dev_total_cum = dev_total_cum + new_contributions.dev; + } + let new_dev_contribution = GuildPoints{cum_score: cum_score_dev, data: contribution_data_dev}; + + + let old_design_contribution = old_contribution.design; + let mut contribution_data_design = old_design_contribution.data; + let mut cum_score_design = old_design_contribution.cum_score; + if (new_contributions.design != 0) { + + contribution_data_design.append(month_id); + contribution_data_design.append(new_contributions.design); + + cum_score_design = cum_score_design + new_contributions.design; + + design_total_cum = design_total_cum + new_contributions.design; + } + let new_design_contribution = GuildPoints{cum_score: cum_score_design, data: contribution_data_design}; + + + let old_problem_solving_contribution = old_contribution.problem_solving; + let mut contribution_data_problem_solving = old_problem_solving_contribution.data; + let mut cum_score_problem_solving = old_problem_solving_contribution.cum_score; + if (new_contributions.problem_solving != 0) { + + contribution_data_problem_solving.append(month_id); + contribution_data_problem_solving.append(new_contributions.problem_solving); + + cum_score_problem_solving = cum_score_problem_solving + new_contributions.problem_solving; + + problem_solving_total_cum = problem_solving_total_cum + new_contributions.problem_solving; + } + let new_problem_solving_contribution = GuildPoints{cum_score: cum_score_problem_solving, data: contribution_data_problem_solving}; + + + let old_marcom_contribution = old_contribution.marcom; + let mut contribution_data_marcom = old_marcom_contribution.data; + let mut cum_score_marcom = old_marcom_contribution.cum_score; + if (new_contributions.marcom != 0) { + + contribution_data_marcom.append(month_id); + contribution_data_marcom.append(new_contributions.marcom); + + cum_score_marcom = cum_score_marcom + new_contributions.marcom; + marcom_total_cum = marcom_total_cum + new_contributions.marcom; + } + let new_marcom_contribution = GuildPoints{cum_score: cum_score_marcom, data: contribution_data_marcom}; + + + let old_research_contribution = old_contribution.research; + let mut contribution_data_research = old_research_contribution.data; + let mut cum_score_research = old_research_contribution.cum_score; + if (new_contributions.research != 0) { + + contribution_data_research.append(month_id); + contribution_data_research.append(new_contributions.research); + + cum_score_research = cum_score_research + new_contributions.research; + + research_total_cum = research_total_cum + new_contributions.research; + } + let new_research_contribution = GuildPoints{cum_score: cum_score_research, data: contribution_data_research}; + + + let updated_contribution = Contribution{dev: new_dev_contribution, design: new_design_contribution, problem_solving: new_problem_solving_contribution, marcom: new_marcom_contribution, research: new_research_contribution, last_timestamp: block_timestamp}; + self._contributions.write(contributor, updated_contribution); + + let total_monthy_contribution = TotalMonthlyContribution{dev: dev_total_cum, design: design_total_cum, problem_solving: problem_solving_total_cum, marcom: marcom_total_cum, research: research_total_cum}; + self._total_contribution.write(month_id, total_monthy_contribution); + current_index += 1; + // } + self.emit(ContributionUpdated{update_id: id, contributor: contributor, month_id: month_id, points_earned: new_contributions}); + + }; + id += 1; + self._last_update_id.write(id); + self._last_update_time.write(block_timestamp); + + } + + + fn migrate_points_initiated_by_DAO(ref self: ContractState, old_addresses: Array::, new_addresses: Array:: ) { + self._only_owner(); + assert(old_addresses.len() == new_addresses.len(), 'INVALID_INPUTS'); + let mut current_index = 0; + + loop { + if (current_index == old_addresses.len() - 1) { + break true; + } + InternalImpl::_migrate_points(ref self, *old_addresses[current_index], *new_addresses[current_index]); + current_index += 1; + }; + + } + + + fn migrate_points_initiated_by_holder(ref self: ContractState, new_address: ContractAddress) { + let caller = get_caller_address(); + let migration_hash: felt252 = LegacyHash::hash(caller.into(), new_address); + + self._queued_migrations.write(migration_hash, true); + + self.emit(MigrationQueued { old_address: caller, new_address: new_address, hash: migration_hash}); + + } + + fn execute_migrate_points_initiated_by_holder(ref self: ContractState, old_address: ContractAddress, new_address: ContractAddress) { + self._only_owner(); + let migration_hash: felt252 = LegacyHash::hash(old_address.into(), new_address); + let is_queued = self._queued_migrations.read(migration_hash); + + assert(is_queued == true, 'NOT_QUEUED'); + + self._queued_migrations.write(migration_hash, false); + InternalImpl::_migrate_points(ref self, old_address, new_address); + // self._last_update_id.write(0); + } + + + + + } + + #[generate_trait] + impl InternalImpl of InternalTrait { + // + // Internals + // + + fn _migrate_points(ref self: ContractState, old_address: ContractAddress, new_address: ContractAddress) { + + let design_guild = self._design_guild_SBT.read(); + let dev_guild = self._dev_guild_SBT.read(); + let problem_solver_guild = self._problem_solving_guild_SBT.read(); + let marcom_guild = self._marcom_guild_SBT.read(); + let research_guild = self._research_guild_SBT.read(); + + let contribution = self._contributions.read(old_address); + /// @notice data needs to be empty list, TODO:: figure out how to create that. + let zero_contribution = Contribution{dev: GuildPoints{ cum_score: 0_u32, data: ArrayTrait::new()}, + design: GuildPoints{ cum_score: 0_u32, data: ArrayTrait::new()}, + problem_solving: GuildPoints{ cum_score: 0_u32, data: ArrayTrait::new()}, + marcom: GuildPoints{ cum_score: 0_u32, data:ArrayTrait::new()}, + research: GuildPoints{ cum_score: 0_u32, data: ArrayTrait::new()}, + last_timestamp: 0_u64 + }; + + self._contributions.write(old_address, zero_contribution); + self._contributions.write(new_address, contribution); + + let dev_guildDispatcher = IGuildDispatcher { contract_address: dev_guild }; + dev_guildDispatcher.migrate_sbt(old_address, new_address); + + let design_guildDispatcher = IGuildDispatcher { contract_address: design_guild }; + design_guildDispatcher.migrate_sbt(old_address, new_address); + + let problem_solver_guildDispatcher = IGuildDispatcher { contract_address: problem_solver_guild }; + problem_solver_guildDispatcher.migrate_sbt(old_address, new_address); + + let marcom_guildDispatcher = IGuildDispatcher { contract_address: marcom_guild }; + marcom_guildDispatcher.migrate_sbt(old_address, new_address); + + let research_guildDispatcher = IGuildDispatcher { contract_address: research_guild }; + research_guildDispatcher.migrate_sbt(old_address, new_address); + + self.emit(Migrated{old_address: old_address, new_address: new_address}); + + } + + } + + + + #[generate_trait] + impl ModifierImpl of ModifierTrait { + fn _only_owner(self: @ContractState) { + let mut ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.assert_only_owner(); + } + } + + #[external(v0)] + impl IOwnableImpl of IOwnable { + fn owner(self: @ContractState) -> ContractAddress { + let ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.owner() + } + + fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { + let mut ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.transfer_ownership(:new_owner); + } + + fn renounce_ownership(ref self: ContractState) { + let mut ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.renounce_ownership(); + } + } + + + +} From 372707f903bb3c33368c8f3151047c78c1a64d58 Mon Sep 17 00:00:00 2001 From: yashm001 Date: Thu, 12 Oct 2023 12:12:51 +0530 Subject: [PATCH 12/22] added tests --- src/Master.cairo | 5 +- tests/test_deployment.cairo | 97 +++++++ tests/test_migrate_points.cairo | 298 ++++++++++++++++++++ tests/test_mint.cairo | 146 ++++++++++ tests/test_update_contribution_points.cairo | 97 +++++++ tests/utils.cairo | 27 ++ 6 files changed, 668 insertions(+), 2 deletions(-) create mode 100644 tests/test_deployment.cairo create mode 100644 tests/test_migrate_points.cairo create mode 100644 tests/test_mint.cairo create mode 100644 tests/test_update_contribution_points.cairo create mode 100644 tests/utils.cairo diff --git a/src/Master.cairo b/src/Master.cairo index 7263ab5..784d2f7 100644 --- a/src/Master.cairo +++ b/src/Master.cairo @@ -312,7 +312,7 @@ mod Master { let mut research_total_cum = 0_u32; loop { - if (current_index == contributions.len() - 1) { + if (current_index == contributions.len()) { break true; } let new_contributions: MonthlyContribution = *contributions[current_index]; @@ -401,7 +401,7 @@ mod Master { let mut current_index = 0; loop { - if (current_index == old_addresses.len() - 1) { + if (current_index == old_addresses.len()) { break true; } InternalImpl::_migrate_points(ref self, *old_addresses[current_index], *new_addresses[current_index]); @@ -412,6 +412,7 @@ mod Master { fn migrate_points_initiated_by_holder(ref self: ContractState, new_address: ContractAddress) { + // TODO: if new address already have any contribution points, if yes return. let caller = get_caller_address(); let migration_hash: felt252 = LegacyHash::hash(caller.into(), new_address); diff --git a/tests/test_deployment.cairo b/tests/test_deployment.cairo new file mode 100644 index 0000000..cedeec7 --- /dev/null +++ b/tests/test_deployment.cairo @@ -0,0 +1,97 @@ +use array::{Array, ArrayTrait, SpanTrait}; +use result::ResultTrait; +use starknet::ContractAddress; +use starknet::ClassHash; +use traits::TryInto; +use option::OptionTrait; +use snforge_std::{ declare, ContractClassTrait }; +use tests::utils::{ deployer_addr, user1}; + + +#[starknet::interface] +trait IMaster { + fn owner(self: @T) -> ContractAddress; +} + +#[starknet::interface] +trait IGuildSBT { + fn name(self: @T) -> felt252; + fn symbol(self: @T) -> felt252; + fn get_master(self: @T) -> ContractAddress; + fn owner(self: @T) -> ContractAddress; + fn baseURI(self: @T) -> Span; + fn get_contribution_levels(self: @T) -> Array; + fn get_number_of_levels(self: @T) -> u32; + +} + +fn URI() -> Span { + let mut uri = ArrayTrait::new(); + + uri.append('api.jediswap/'); + uri.append('guildSBT/'); + uri.append('dev/'); + + uri.span() +} + +#[test] +fn test_deployment_master_guildSBT() { + let mut master_constructor_calldata = Default::default(); + Serde::serialize(@deployer_addr(), ref master_constructor_calldata); + let master_class = declare('Master'); + let master_address = master_class.deploy(@master_constructor_calldata).unwrap(); + + let name = 'Jedi Dev Guild SBT'; + let symbol = 'JEDI-DEV'; + let mut contribution_levels = ArrayTrait::new(); + contribution_levels.append(100); + contribution_levels.append(200); + contribution_levels.append(500); + contribution_levels.append(1000); + + let mut guildSBT_constructor_calldata = Default::default(); + Serde::serialize(@name, ref guildSBT_constructor_calldata); + Serde::serialize(@symbol, ref guildSBT_constructor_calldata); + Serde::serialize(@URI(), ref guildSBT_constructor_calldata); + Serde::serialize(@deployer_addr(), ref guildSBT_constructor_calldata); + Serde::serialize(@master_address, ref guildSBT_constructor_calldata); + Serde::serialize(@contribution_levels, ref guildSBT_constructor_calldata); + + let guildSBT_class = declare('GuildSBT'); + let guildSBT_address = guildSBT_class.deploy(@guildSBT_constructor_calldata).unwrap(); + + // Create a Dispatcher object that will allow interacting with the deployed contract + let master_dispatcher = IMasterDispatcher { contract_address: master_address }; + + let result = master_dispatcher.owner(); + assert(result == deployer_addr(), 'Invalid Owner'); + + let guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: guildSBT_address }; + + let result = guildSBT_dispatcher.get_master(); + assert(result == master_address, 'Invalid Master'); + + let name: felt252 = guildSBT_dispatcher.name(); + assert(name == 'Jedi Dev Guild SBT', 'Invalid name'); + + let symbol: felt252 = guildSBT_dispatcher.symbol(); + assert(symbol == 'JEDI-DEV', 'Invalid symbol'); + + let baseURI: Span = guildSBT_dispatcher.baseURI(); + // TODO: compare span with felt252 + // assert(baseURI == 'api.jediswap/guildSBT/dev/', 'Invalid base uri'); + + let owner = guildSBT_dispatcher.owner(); + assert(owner == deployer_addr(), 'Invalid Owner'); + + let levels = guildSBT_dispatcher.get_contribution_levels(); + assert(*levels[0] == 100 , 'Invlalid level 0'); + assert(*levels[1] == 200 , 'Invlalid level 1'); + assert(*levels[2] == 500 , 'Invlalid level 2'); + assert(*levels[3] == 1000 , 'Invlalid level 3'); + + let number_of_levels = guildSBT_dispatcher.get_number_of_levels(); + assert(number_of_levels == 4, 'Invalid levels'); + +} \ No newline at end of file diff --git a/tests/test_migrate_points.cairo b/tests/test_migrate_points.cairo new file mode 100644 index 0000000..c945f8e --- /dev/null +++ b/tests/test_migrate_points.cairo @@ -0,0 +1,298 @@ +use array::{Array, ArrayTrait, SpanTrait}; +use result::ResultTrait; +use starknet::ContractAddress; +use starknet::ClassHash; +use traits::TryInto; +use option::OptionTrait; +use snforge_std::{ declare, ContractClassTrait, ContractClass, start_warp, start_prank, stop_prank, + spy_events, SpyOn, EventSpy, EventFetcher, Event, EventAssertions }; +use tests::utils::{ deployer_addr, user1, user2, user3, user4}; +use contributor_SBT2_0::Master::MonthlyContribution; +use contributor_SBT2_0::Master::Contribution; + +#[starknet::interface] +trait IMaster { + fn get_last_update_id(self: @TContractState) -> u32; + fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_contibutions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; + + fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); + fn migrate_points_initiated_by_DAO(ref self: TContractState, old_addresses: Array::, new_addresses: Array:: ); + fn initialise(ref self: TContractState, dev_guild: ContractAddress, design_guild: ContractAddress, marcom_guild: ContractAddress, problem_solver_guild: ContractAddress, research_guild: ContractAddress); + fn migrate_points_initiated_by_holder(ref self: TContractState, new_address: ContractAddress); + fn execute_migrate_points_initiated_by_holder(ref self: TContractState, old_address: ContractAddress, new_address: ContractAddress); + +} + +#[starknet::interface] +trait IGuildSBT { + fn balance_of(self: @TContractState, account: ContractAddress) -> u256; + fn wallet_of_owner(self: @TContractState, account: ContractAddress) -> u256; + fn get_contribution_tier(self: @TContractState, contributor: ContractAddress) -> u32; + fn tokenURI(self: @TContractState, token_id: u256) -> Span; + + fn safe_mint(ref self: TContractState, token_type: u8); + +} + + +fn URI() -> Span { + let mut uri = ArrayTrait::new(); + + uri.append('api.jediswap/'); + uri.append('guildSBT/'); + uri.append('dev/'); + + uri.span() +} + +fn deploy_contracts_and_initialise() -> (ContractAddress, ContractAddress, ContractAddress, ContractAddress, ContractAddress, ContractAddress) { + let mut master_constructor_calldata = Default::default(); + Serde::serialize(@deployer_addr(), ref master_constructor_calldata); + let master_class = declare('Master'); + let master_address = master_class.deploy(@master_constructor_calldata).unwrap(); + + // for simplicity deploying all five guild SBTs with same constructors args. + let name = 'Jedi Dev Guild SBT'; + let symbol = 'JEDI-DEV'; + let mut contribution_levels: Array = ArrayTrait::new(); + contribution_levels.append(100); + contribution_levels.append(200); + contribution_levels.append(500); + contribution_levels.append(1000); + + let mut guildSBT_constructor_calldata = Default::default(); + Serde::serialize(@name, ref guildSBT_constructor_calldata); + Serde::serialize(@symbol, ref guildSBT_constructor_calldata); + Serde::serialize(@URI(), ref guildSBT_constructor_calldata); + Serde::serialize(@deployer_addr(), ref guildSBT_constructor_calldata); + Serde::serialize(@master_address, ref guildSBT_constructor_calldata); + Serde::serialize(@contribution_levels, ref guildSBT_constructor_calldata); + + let guildSBT_class = declare('GuildSBT'); + + let dev_guildSBT_address = guildSBT_class.deploy(@guildSBT_constructor_calldata).unwrap(); + let design_guildSBT_address = guildSBT_class.deploy(@guildSBT_constructor_calldata).unwrap(); + let marcom_guildSBT_address = guildSBT_class.deploy(@guildSBT_constructor_calldata).unwrap(); + let problem_solving_guildSBT_address = guildSBT_class.deploy(@guildSBT_constructor_calldata).unwrap(); + let research_guildSBT_address = guildSBT_class.deploy(@guildSBT_constructor_calldata).unwrap(); + + let master_dispatcher = IMasterDispatcher { contract_address: master_address }; + + start_prank(master_address, deployer_addr()); + master_dispatcher.initialise(dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address); + stop_prank(master_address); + + + + (master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address) +} + +fn update_contribution_and_minting_sbt(master_address: ContractAddress, dev_guildSBT_address: ContractAddress, design_guildSBT_address: ContractAddress, marcom_guildSBT_address: ContractAddress, problem_solving_guildSBT_address: ContractAddress, research_guildSBT_address: ContractAddress) -> (MonthlyContribution, MonthlyContribution) { + let master_dispatcher = IMasterDispatcher { contract_address: master_address }; + + let user1_contribution = MonthlyContribution{ contributor: user1(), dev: 120, design: 250, marcom: 20, problem_solving: 30, research: 10}; + let user2_contribution = MonthlyContribution{ contributor: user2(), dev: 200, design: 30, marcom: 160, problem_solving: 0, research: 50}; + + let mut contributions: Array = ArrayTrait::new(); + contributions.append(user1_contribution); + contributions.append(user2_contribution); + + start_prank(master_address, deployer_addr()); + master_dispatcher.update_contibutions(092023, contributions); + stop_prank(master_address); + + let dev_guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: dev_guildSBT_address }; + let design_guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: design_guildSBT_address }; + let marcom_guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: marcom_guildSBT_address }; + + // minting dev and design SBT for user1 + start_prank(dev_guildSBT_address, user1()); + dev_guildSBT_dispatcher.safe_mint(1); + stop_prank(dev_guildSBT_address); + + start_prank(design_guildSBT_address, user1()); + design_guildSBT_dispatcher.safe_mint(1); + stop_prank(design_guildSBT_address); + + + // minting dev and marcom SBT for user2 + start_prank(dev_guildSBT_address, user2()); + dev_guildSBT_dispatcher.safe_mint(2); + stop_prank(dev_guildSBT_address); + + start_prank(marcom_guildSBT_address, user2()); + marcom_guildSBT_dispatcher.safe_mint(2); + stop_prank(marcom_guildSBT_address); + + // checking the sbt balance for users + let mut result = dev_guildSBT_dispatcher.balance_of(user1()); + assert(result == 1, ''); + result = design_guildSBT_dispatcher.balance_of(user1()); + assert(result == 1, ''); + result = dev_guildSBT_dispatcher.balance_of(user2()); + assert(result == 1, ''); + result = marcom_guildSBT_dispatcher.balance_of(user2()); + assert(result == 1, ''); + + (user1_contribution, user2_contribution) +} + +#[test] +fn test_migrate_points_initiated_by_DAO() { + let (master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address) = deploy_contracts_and_initialise(); + let (user1_contribution, user2_contribution) = update_contribution_and_minting_sbt(master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address); + + let master_dispatcher = IMasterDispatcher { contract_address: master_address }; + let dev_guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: dev_guildSBT_address }; + let design_guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: design_guildSBT_address }; + let marcom_guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: marcom_guildSBT_address }; + + // noting the tokenID for user_1 + let user_1_dev_token_id = dev_guildSBT_dispatcher.wallet_of_owner(user1()); + let user_1_design_token_id = design_guildSBT_dispatcher.wallet_of_owner(user1()); + + // noting the tokenID for user_2 + let user_2_dev_token_id = dev_guildSBT_dispatcher.wallet_of_owner(user2()); + let user_2_marcom_token_id = marcom_guildSBT_dispatcher.wallet_of_owner(user2()); + + // migrating user1 -> user3 and user2 -> user4 + let mut old_addresses = ArrayTrait::new(); + old_addresses.append(user1()); + old_addresses.append(user2()); + + let mut new_addresses = ArrayTrait::new(); + new_addresses.append(user3()); + new_addresses.append(user4()); + + start_prank(master_address, deployer_addr()); + master_dispatcher.migrate_points_initiated_by_DAO(old_addresses, new_addresses); + stop_prank(master_address); + + + // verifying points are successfully migrated + let user3_contribution: Contribution = master_dispatcher.get_contibutions_points(user3()); + assert(user3_contribution.dev == user1_contribution.dev, ''); + assert(user3_contribution.design == user1_contribution.design, ''); + assert(user3_contribution.marcom == user1_contribution.marcom, ''); + assert(user3_contribution.problem_solving == user1_contribution.problem_solving, ''); + assert(user3_contribution.research == user1_contribution.research, ''); + + let user4_contribution: Contribution = master_dispatcher.get_contibutions_points(user4()); + assert(user4_contribution.dev == user2_contribution.dev, ''); + assert(user4_contribution.design == user2_contribution.design, ''); + assert(user4_contribution.marcom == user2_contribution.marcom, ''); + assert(user4_contribution.problem_solving == user2_contribution.problem_solving, ''); + assert(user4_contribution.research == user2_contribution.research, ''); + + + // verfying points of old addresses is resetted to zero + let user1_contribution_updated: Contribution = master_dispatcher.get_contibutions_points(user1()); + assert(user1_contribution_updated.dev == 0, ''); + assert(user1_contribution_updated.design == 0, ''); + assert(user1_contribution_updated.marcom == 0, ''); + assert(user1_contribution_updated.problem_solving == 0, ''); + assert(user1_contribution_updated.research == 0, ''); + + let user2_contribution_updated: Contribution = master_dispatcher.get_contibutions_points(user2()); + assert(user2_contribution_updated.dev == 0, ''); + assert(user2_contribution_updated.design == 0, ''); + assert(user2_contribution_updated.marcom == 0, ''); + assert(user2_contribution_updated.problem_solving == 0, ''); + assert(user2_contribution_updated.research == 0, ''); + + // verifying SBTs are transfered + let mut result = dev_guildSBT_dispatcher.balance_of(user1()); + assert(result == 0, ''); + result = design_guildSBT_dispatcher.balance_of(user1()); + assert(result == 0, ''); + result = dev_guildSBT_dispatcher.balance_of(user2()); + assert(result == 0, ''); + result = marcom_guildSBT_dispatcher.balance_of(user2()); + assert(result == 0, ''); + + result = dev_guildSBT_dispatcher.balance_of(user3()); + assert(result == 1, ''); + result = design_guildSBT_dispatcher.balance_of(user3()); + assert(result == 1, ''); + result = dev_guildSBT_dispatcher.balance_of(user4()); + assert(result == 1, ''); + result = marcom_guildSBT_dispatcher.balance_of(user4()); + assert(result == 1, ''); + + // verifying correct token id is transfered + let user_3_dev_token_id = dev_guildSBT_dispatcher.wallet_of_owner(user3()); + let user_3_design_token_id = design_guildSBT_dispatcher.wallet_of_owner(user3()); + + let user_4_dev_token_id = dev_guildSBT_dispatcher.wallet_of_owner(user4()); + let user_4_marcom_token_id = marcom_guildSBT_dispatcher.wallet_of_owner(user4()); + + assert(user_3_dev_token_id == user_1_dev_token_id, ''); + assert(user_3_design_token_id == user_1_design_token_id, ''); + assert(user_4_dev_token_id == user_4_dev_token_id, ''); + assert(user_4_marcom_token_id == user_4_marcom_token_id, ''); + +} + +#[test] +fn test_migrate_points_initiated_by_holder() { + let (master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address) = deploy_contracts_and_initialise(); + let (user1_contribution, user2_contribution) = update_contribution_and_minting_sbt(master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address); + + let master_dispatcher = IMasterDispatcher { contract_address: master_address }; + let dev_guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: dev_guildSBT_address }; + let design_guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: design_guildSBT_address }; + let marcom_guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: marcom_guildSBT_address }; + + // noting the tokenID for user_1 + let user_1_dev_token_id = dev_guildSBT_dispatcher.wallet_of_owner(user1()); + let user_1_design_token_id = design_guildSBT_dispatcher.wallet_of_owner(user1()); + + // initiating migration request + start_prank(master_address, user1()); + master_dispatcher.migrate_points_initiated_by_holder(user3()); + stop_prank(master_address); + + // executing migration (by DAO) + start_prank(master_address, deployer_addr()); + master_dispatcher.execute_migrate_points_initiated_by_holder(user1(), user3()); + stop_prank(master_address); + + + // verifying points are successfully migrated + let user3_contribution: Contribution = master_dispatcher.get_contibutions_points(user3()); + assert(user3_contribution.dev == user1_contribution.dev, ''); + assert(user3_contribution.design == user1_contribution.design, ''); + assert(user3_contribution.marcom == user1_contribution.marcom, ''); + assert(user3_contribution.problem_solving == user1_contribution.problem_solving, ''); + assert(user3_contribution.research == user1_contribution.research, ''); + + // verfying points of old addresses is resetted to zero + let user1_contribution_updated: Contribution = master_dispatcher.get_contibutions_points(user1()); + assert(user1_contribution_updated.dev == 0, ''); + assert(user1_contribution_updated.design == 0, ''); + assert(user1_contribution_updated.marcom == 0, ''); + assert(user1_contribution_updated.problem_solving == 0, ''); + assert(user1_contribution_updated.research == 0, ''); + + + // verifying SBTs are transfered + let mut result = dev_guildSBT_dispatcher.balance_of(user1()); + assert(result == 0, ''); + result = design_guildSBT_dispatcher.balance_of(user1()); + assert(result == 0, ''); + + result = dev_guildSBT_dispatcher.balance_of(user3()); + assert(result == 1, ''); + result = design_guildSBT_dispatcher.balance_of(user3()); + assert(result == 1, ''); + + // verifying correct token id is transfered + let user_3_dev_token_id = dev_guildSBT_dispatcher.wallet_of_owner(user3()); + let user_3_design_token_id = design_guildSBT_dispatcher.wallet_of_owner(user3()); + + assert(user_3_dev_token_id == user_1_dev_token_id, ''); + assert(user_3_design_token_id == user_1_design_token_id, ''); + + +} \ No newline at end of file diff --git a/tests/test_mint.cairo b/tests/test_mint.cairo new file mode 100644 index 0000000..9e63d20 --- /dev/null +++ b/tests/test_mint.cairo @@ -0,0 +1,146 @@ +use array::{Array, ArrayTrait, SpanTrait}; +use result::ResultTrait; +use starknet::ContractAddress; +use starknet::ClassHash; +use traits::TryInto; +use option::OptionTrait; +use snforge_std::{ declare, ContractClassTrait, ContractClass, start_warp, start_prank, stop_prank, + spy_events, SpyOn, EventSpy, EventFetcher, Event, EventAssertions }; +use tests::utils::{ deployer_addr, user1, user2}; +use contributor_SBT2_0::Master::MonthlyContribution; + +#[starknet::interface] +trait IMaster { + fn get_last_update_id(self: @TContractState) -> u32; + fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> u32; + + fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); + +} + +#[starknet::interface] +trait IGuildSBT { + fn balance_of(self: @TContractState, account: ContractAddress) -> u256; + fn wallet_of_owner(self: @TContractState, account: ContractAddress) -> u256; + fn get_contribution_tier(self: @TContractState, contributor: ContractAddress) -> u32; + fn tokenURI(self: @TContractState, token_id: u256) -> Span; + + fn safe_mint(ref self: TContractState, token_type: u8); +} + + +fn URI() -> Span { + let mut uri = ArrayTrait::new(); + + uri.append('api.jediswap/'); + uri.append('guildSBT/'); + uri.append('dev/'); + + uri.span() +} + +fn deploy_contracts() -> (ContractAddress, ContractAddress) { + let mut master_constructor_calldata = Default::default(); + Serde::serialize(@deployer_addr(), ref master_constructor_calldata); + let master_class = declare('Master'); + let master_address = master_class.deploy(@master_constructor_calldata).unwrap(); + + let name = 'Jedi Dev Guild SBT'; + let symbol = 'JEDI-DEV'; + let mut contribution_levels: Array = ArrayTrait::new(); + contribution_levels.append(100); + contribution_levels.append(200); + contribution_levels.append(500); + contribution_levels.append(1000); + + let mut guildSBT_constructor_calldata = Default::default(); + Serde::serialize(@name, ref guildSBT_constructor_calldata); + Serde::serialize(@symbol, ref guildSBT_constructor_calldata); + Serde::serialize(@URI(), ref guildSBT_constructor_calldata); + Serde::serialize(@deployer_addr(), ref guildSBT_constructor_calldata); + Serde::serialize(@master_address, ref guildSBT_constructor_calldata); + Serde::serialize(@contribution_levels, ref guildSBT_constructor_calldata); + + let guildSBT_class = declare('GuildSBT'); + let guildSBT_address = guildSBT_class.deploy(@guildSBT_constructor_calldata).unwrap(); + + (master_address, guildSBT_address) +} + +#[test] +#[should_panic(expected: ('NOT_ENOUGH_POINTS', ))] +fn test_mint_not_enough_contribution_points() { + let (_, guildSBT_address) = deploy_contracts(); + let guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: guildSBT_address }; + + let balance = guildSBT_dispatcher.balance_of(user1()); + assert(balance == 0, 'invlaid initialisation'); + + start_prank(guildSBT_address, user1()); + guildSBT_dispatcher.safe_mint(1); + stop_prank(guildSBT_address); + +} + +#[test] +fn test_mint() { + let (master_address, guildSBT_address) = deploy_contracts(); + let master_dispatcher = IMasterDispatcher { contract_address: master_address }; + let guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: guildSBT_address }; + + let balance = guildSBT_dispatcher.balance_of(user1()); + assert(balance == 0, 'invlaid initialisation'); + + let mut contributions: Array = ArrayTrait::new(); + contributions.append(MonthlyContribution{ contributor: user1(), dev: 240, design: 250, marcom: 20, problem_solving: 30, research: 10}); + contributions.append(MonthlyContribution{ contributor: user2(), dev: 200, design: 30, marcom: 0, problem_solving: 0, research: 50}); + + start_prank(master_address, deployer_addr()); + master_dispatcher.update_contibutions(092023, contributions); + stop_prank(master_address); + + start_prank(guildSBT_address, user1()); + guildSBT_dispatcher.safe_mint(1); + stop_prank(guildSBT_address); + + let new_balance = guildSBT_dispatcher.balance_of(user1()); + assert(new_balance == 1, 'invalid balance'); + + let user1_token_id = guildSBT_dispatcher.wallet_of_owner(user1()); + + let tokenURI = guildSBT_dispatcher.tokenURI(user1_token_id); + // TODO: comapre span to string + // assert(tokenURI == 'api.jediswap/guildSBT/dev/21', 'invalid uri'); + +} + +#[test] +#[should_panic(expected: ('ALREADY_MINTED', ))] +fn test_mint_second_sbt() { + let (master_address, guildSBT_address) = deploy_contracts(); + let master_dispatcher = IMasterDispatcher { contract_address: master_address }; + let guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: guildSBT_address }; + + let balance = guildSBT_dispatcher.balance_of(user1()); + assert(balance == 0, 'invlaid initialisation'); + + let mut contributions: Array = ArrayTrait::new(); + contributions.append(MonthlyContribution{ contributor: user1(), dev: 240, design: 250, marcom: 20, problem_solving: 30, research: 10}); + contributions.append(MonthlyContribution{ contributor: user2(), dev: 200, design: 30, marcom: 0, problem_solving: 0, research: 50}); + + start_prank(master_address, deployer_addr()); + master_dispatcher.update_contibutions(092023, contributions); + stop_prank(master_address); + + start_prank(guildSBT_address, user1()); + guildSBT_dispatcher.safe_mint(1); + stop_prank(guildSBT_address); + + let new_balance = guildSBT_dispatcher.balance_of(user1()); + assert(new_balance == 1, 'invalid balance'); + + start_prank(guildSBT_address, user1()); + guildSBT_dispatcher.safe_mint(1); + stop_prank(guildSBT_address); + +} \ No newline at end of file diff --git a/tests/test_update_contribution_points.cairo b/tests/test_update_contribution_points.cairo new file mode 100644 index 0000000..6079aa9 --- /dev/null +++ b/tests/test_update_contribution_points.cairo @@ -0,0 +1,97 @@ +use array::{Array, ArrayTrait, SpanTrait}; +use result::ResultTrait; +use starknet::ContractAddress; +use starknet::ClassHash; +use traits::TryInto; +use option::OptionTrait; +use snforge_std::{ declare, ContractClassTrait, ContractClass, start_warp, start_prank, stop_prank, + spy_events, SpyOn, EventSpy, EventFetcher, Event, EventAssertions }; +use tests::utils::{ deployer_addr, user1, user2}; +use contributor_SBT2_0::Master::MonthlyContribution; +use contributor_SBT2_0::Master::Contribution; +use contributor_SBT2_0::Master::TotalMonthlyContribution; + +#[starknet::interface] +trait IMaster { + fn get_last_update_id(self: @TContractState) -> u32; + fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_contibutions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; + fn get_contributions_data(self: @TContractState, contributor: ContractAddress) -> Array; + + fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); + +/////// + +} + +#[starknet::interface] +trait IGuildSBT { + fn get_contribution_tier(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_master(self: @TContractState) -> ContractAddress; +} + +fn URI() -> Span { + let mut uri = ArrayTrait::new(); + + uri.append('api.jediswap/'); + uri.append('guildSBT/'); + uri.append('dev/'); + + uri.span() +} + +fn deploy_contracts() -> (ContractAddress, ContractAddress) { + let mut master_constructor_calldata = Default::default(); + Serde::serialize(@deployer_addr(), ref master_constructor_calldata); + let master_class = declare('Master'); + let master_address = master_class.deploy(@master_constructor_calldata).unwrap(); + + let name = 'Jedi Dev Guild SBT'; + let symbol = 'JEDI-DEV'; + let mut contribution_levels: Array = ArrayTrait::new(); + contribution_levels.append(100); + contribution_levels.append(200); + contribution_levels.append(500); + contribution_levels.append(1000); + + let mut guildSBT_constructor_calldata = Default::default(); + Serde::serialize(@name, ref guildSBT_constructor_calldata); + Serde::serialize(@symbol, ref guildSBT_constructor_calldata); + Serde::serialize(@URI(), ref guildSBT_constructor_calldata); + Serde::serialize(@deployer_addr(), ref guildSBT_constructor_calldata); + Serde::serialize(@master_address, ref guildSBT_constructor_calldata); + Serde::serialize(@contribution_levels, ref guildSBT_constructor_calldata); + + let guildSBT_class = declare('GuildSBT'); + let guildSBT_address = guildSBT_class.deploy(@guildSBT_constructor_calldata).unwrap(); + + (master_address, guildSBT_address) +} + +#[test] +fn test_update_contribution_points() { + let (master_address, guildSBT_address) = deploy_contracts(); + let master_dispatcher = IMasterDispatcher { contract_address: master_address }; + let guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: guildSBT_address }; + + let id1 = master_dispatcher.get_last_update_id(); + assert (id1 == 0, 'Invalid id initialisation'); + + let mut contributions: Array = ArrayTrait::new(); + contributions.append(MonthlyContribution{ contributor: user1(), dev: 120, design: 250, marcom: 20, problem_solving: 30, research: 10}); + contributions.append(MonthlyContribution{ contributor: user2(), dev: 200, design: 30, marcom: 0, problem_solving: 0, research: 50}); + + start_prank(master_address, deployer_addr()); + master_dispatcher.update_contibutions(092023, contributions); + stop_prank(master_address); + + let id2 = master_dispatcher.get_last_update_id(); + assert (id2 == 1, 'invalid id'); + + let tier1 = guildSBT_dispatcher.get_contribution_tier(user1()); + assert (tier1 == 1, 'invalid tier1'); + + let tier2 = guildSBT_dispatcher.get_contribution_tier(user2()); + assert (tier2 == 2, 'invalid tier2'); + +} \ No newline at end of file diff --git a/tests/utils.cairo b/tests/utils.cairo new file mode 100644 index 0000000..78d0b5b --- /dev/null +++ b/tests/utils.cairo @@ -0,0 +1,27 @@ +use starknet:: { ContractAddress, contract_address_try_from_felt252, contract_address_const }; + + +fn deployer_addr() -> ContractAddress { + contract_address_try_from_felt252('deployer').unwrap() +} + + +fn zero_addr() -> ContractAddress { + contract_address_const::<0>() +} + +fn user1() -> ContractAddress { + contract_address_try_from_felt252('user1').unwrap() +} + +fn user2() -> ContractAddress { + contract_address_try_from_felt252('user2').unwrap() +} + +fn user3() -> ContractAddress { + contract_address_try_from_felt252('user3').unwrap() +} + +fn user4() -> ContractAddress { + contract_address_try_from_felt252('user4').unwrap() +} \ No newline at end of file From 2ca72e12a022431e8a9ae468531ea3ad0981b78b Mon Sep 17 00:00:00 2001 From: yashm001 Date: Thu, 12 Oct 2023 12:22:25 +0530 Subject: [PATCH 13/22] Update GuildSBT.cairo --- src/GuildSBT.cairo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GuildSBT.cairo b/src/GuildSBT.cairo index 4e1c015..bfdab24 100644 --- a/src/GuildSBT.cairo +++ b/src/GuildSBT.cairo @@ -4,7 +4,6 @@ // @notice SBT contract to give out to contributor use starknet::ContractAddress; -use zeroable::Zeroable; use array::{Array, ArrayTrait, SpanTrait}; use serde::Serde; use traits::{Into, TryInto}; @@ -114,6 +113,7 @@ mod GuildSBT { let owner = erc721_self.owner_of(:token_id); let master = self._master.read(); let masterDispatcher = IMasterDispatcher { contract_address: master }; + // @notice this is a sample SBT contract for dev guild, update the next line before deploying other guild let points = masterDispatcher.get_dev_points(owner); let token_type = self._token_type.read(owner); @@ -128,6 +128,7 @@ mod GuildSBT { fn tokenURI_from_contributor(self: @ContractState, contributor: ContractAddress) -> Span { let master = self._master.read(); let masterDispatcher = IMasterDispatcher { contract_address: master }; + // @notice this is a sample SBT contract for dev guild, update the next line before deploying other guild let points = masterDispatcher.get_dev_points(contributor); let token_type = self._token_type.read(contributor); From dd12ede3226120d725b075f995327bf6e84a0902 Mon Sep 17 00:00:00 2001 From: yashm001 Date: Thu, 12 Oct 2023 12:26:40 +0530 Subject: [PATCH 14/22] Update Master_approach1.cairo --- src/Master_approach1.cairo | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Master_approach1.cairo b/src/Master_approach1.cairo index 3a21f2e..9273a88 100644 --- a/src/Master_approach1.cairo +++ b/src/Master_approach1.cairo @@ -3,6 +3,11 @@ // @license MIT // @notice Master to store contribution points +// ************************************ +// @notice this approach is abandoned because issue with storing of array +// Can refer it in future (if possible to implement) +// ************************************ + use starknet::ContractAddress; use zeroable::Zeroable; use array::{Array, ArrayTrait, SpanTrait}; From dcba23c012f68bc456141126bda04a8151eb65cb Mon Sep 17 00:00:00 2001 From: yashm001 Date: Thu, 12 Oct 2023 12:37:53 +0530 Subject: [PATCH 15/22] making sbt non transferable --- src/GuildSBT.cairo | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/GuildSBT.cairo b/src/GuildSBT.cairo index bfdab24..d1861c4 100644 --- a/src/GuildSBT.cairo +++ b/src/GuildSBT.cairo @@ -342,9 +342,10 @@ mod GuildSBT { fn transfer_from( ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 ) { - let mut erc721_self = ERC721::unsafe_new_contract_state(); + assert(false, 'SBT_NOT_TRANSFERABLE'); + // let mut erc721_self = ERC721::unsafe_new_contract_state(); - erc721_self.transfer_from(:from, :to, :token_id); + // erc721_self.transfer_from(:from, :to, :token_id); } fn safe_transfer_from( @@ -354,9 +355,11 @@ mod GuildSBT { token_id: u256, data: Span ) { - let mut erc721_self = ERC721::unsafe_new_contract_state(); + assert(false, 'SBT_NOT_TRANSFERABLE'); + + // let mut erc721_self = ERC721::unsafe_new_contract_state(); - erc721_self.safe_transfer_from(:from, :to, :token_id, :data); + // erc721_self.safe_transfer_from(:from, :to, :token_id, :data); } fn set_approval_for_all( @@ -432,7 +435,9 @@ mod GuildSBT { fn transferFrom( ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 ) { - IERC721::transfer_from(ref self, :from, :to, token_id: tokenId); + assert(false, 'SBT_NOT_TRANSFERABLE'); + + // IERC721::transfer_from(ref self, :from, :to, token_id: tokenId); } fn safeTransferFrom( @@ -442,7 +447,9 @@ mod GuildSBT { tokenId: u256, data: Span ) { - IERC721::safe_transfer_from(ref self, :from, :to, token_id: tokenId, :data); + assert(false, 'SBT_NOT_TRANSFERABLE'); + + // IERC721::safe_transfer_from(ref self, :from, :to, token_id: tokenId, :data); } fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { From d0a3a09f030177eed8c1f282b4526fba7f77fbb2 Mon Sep 17 00:00:00 2001 From: yashm001 Date: Wed, 18 Oct 2023 21:44:18 +0530 Subject: [PATCH 16/22] resolves fixes --- Scarb.toml | 2 +- src/GuildSBT.cairo | 22 +-- src/Master.cairo | 178 +++++++------------- tests/lib.cairo | 5 + tests/test_deployment.cairo | 17 +- tests/test_migrate_points.cairo | 116 +++++++++++-- tests/test_mint.cairo | 44 +++-- tests/test_update_contribution_points.cairo | 134 ++++++++++++--- tests/utils.cairo | 10 ++ 9 files changed, 329 insertions(+), 199 deletions(-) create mode 100644 tests/lib.cairo diff --git a/Scarb.toml b/Scarb.toml index 7999662..3a99615 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -14,5 +14,5 @@ alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandri [dependencies] starknet = ">=2.2.0" -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.5.0" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.7.1" } openzeppelin = { git = "https://github.com/openzeppelin/cairo-contracts", tag = "v0.7.0" } diff --git a/src/GuildSBT.cairo b/src/GuildSBT.cairo index d1861c4..7ea2631 100644 --- a/src/GuildSBT.cairo +++ b/src/GuildSBT.cairo @@ -4,9 +4,8 @@ // @notice SBT contract to give out to contributor use starknet::ContractAddress; -use array::{Array, ArrayTrait, SpanTrait}; -use serde::Serde; -use traits::{Into, TryInto}; +use array::Array; + #[starknet::interface] trait IMaster { @@ -22,6 +21,7 @@ trait IGuildSBT { fn tokenURI(self: @TContractState, token_id: u256) -> Span; fn tokenURI_from_contributor(self: @TContractState, contributor: ContractAddress) -> Span; fn get_master(self: @TContractState) -> ContractAddress; + fn get_next_token_id(self: @TContractState) -> u256; fn get_contribution_tier(self: @TContractState, contributor: ContractAddress) -> u32; fn get_contribution_levels(self: @TContractState) -> Array; fn get_number_of_levels(self: @TContractState) -> u32; @@ -143,6 +143,10 @@ mod GuildSBT { self._master.read() } + fn get_next_token_id(self: @ContractState) -> u256 { + self._next_token_id.read() + } + fn get_contribution_tier(self: @ContractState, contributor: ContractAddress) -> u32 { let master = self._master.read(); @@ -195,13 +199,13 @@ mod GuildSBT { let balance = erc721_self.balance_of(:account); assert (balance == 0, 'ALREADY_MINTED'); - self._token_type.write(account, token_type); let master = self._master.read(); let masterDispatcher = IMasterDispatcher { contract_address: master }; let points = masterDispatcher.get_dev_points(account); let tier = InternalImpl::_get_contribution_tier(@self, points); assert (tier != 0, 'NOT_ENOUGH_POINTS'); + self._token_type.write(account, token_type); let token_id = self._next_token_id.read(); erc721_self._mint(to: account, token_id: token_id.into()); self._wallet_of_owner.write(account, token_id); @@ -232,10 +236,6 @@ mod GuildSBT { } - - - - } #[generate_trait] @@ -249,12 +249,12 @@ mod GuildSBT { let mut current_index = 0_u32; let contribution_levels = self._contribution_levels.read(); loop { - if (current_index == contribution_levels.len() - 1) { - break true; + if (current_index == contribution_levels.len()) { + break; } if (points < *contribution_levels[current_index]) { - break true; + break; } current_index += 1; diff --git a/src/Master.cairo b/src/Master.cairo index 784d2f7..4899c58 100644 --- a/src/Master.cairo +++ b/src/Master.cairo @@ -2,37 +2,24 @@ // @author Mesh Finance // @license MIT // @notice Master to store contribution points +// TODO:: modify the structure to add new guild in future. use starknet::ContractAddress; -use zeroable::Zeroable; -use array::{Array, ArrayTrait, SpanTrait}; -use serde::Serde; -use traits::{Into, TryInto}; -use contributor_SBT2_0::array::StoreU32Array; +use array::Array; - -// #[derive(Drop, Serde, starknet::Store)] -// struct GuildPoints { -// // @notice the cummulative score for each contributor -// cum_score: u32, -// // @notice Monthly points for contribution for eg [Sept_2023 -> 250] will be written as [092023, 250] -// // even index as month_id, immediate right is points in that month -// data: Array -// } - #[derive(Drop, Serde, starknet::Store)] struct Contribution { // @notice Contribution for dev guild - dev: u32,//GuildPoints, + dev: u32, // @notice Contribution for design guild - design: u32,//GuildPoints, + design: u32, // @notice Contribution for problem solving guild - problem_solving: u32,//GuildPoints, + problem_solving: u32, // @notice Contribution for marcom guild - marcom: u32,//GuildPoints, + marcom: u32, // @notice Contribution for research guild - research: u32,//GuildPoints, + research: u32, // @notice timestamp for the last update last_timestamp: u64 } @@ -85,7 +72,7 @@ trait IGuild { #[starknet::interface] trait IMaster { // view functions - fn get_contibutions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; + fn get_contributions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> u32; fn get_design_points(self: @TContractState, contributor: ContractAddress) -> u32; fn get_problem_solving_points(self: @TContractState, contributor: ContractAddress) -> u32; @@ -128,7 +115,6 @@ mod Master { use starknet::{ContractAddress, ClassHash, SyscallResult, SyscallResultTrait, get_caller_address, get_contract_address, get_block_timestamp, contract_address_const}; use integer::{u128_try_from_felt252, u256_sqrt, u256_from_felt252}; use starknet::syscalls::{replace_class_syscall, call_contract_syscall}; - // use alexandria_storage::list::{List, ListTrait}; use contributor_SBT2_0::array::StoreU32Array; use super::{Contribution, MonthlyContribution, TotalMonthlyContribution}; @@ -142,8 +128,8 @@ mod Master { // #[storage] struct Storage { - _contributions: LegacyMap::, // @dev contributions - _contributions_data: LegacyMap::<(ContractAddress, felt252), Array>, // @dev contributions + _contributions: LegacyMap::, // @dev contributions points for each contributor + _contributions_data: LegacyMap::<(ContractAddress, felt252), Array>, // @dev contributions data for specific contributor and guild _total_contribution: LegacyMap::, // @dev total contribution month wise [month_id => points] _last_update_id: u32, // @dev contribution update id _last_update_time: u64, // @dev timestamp for last update @@ -177,8 +163,7 @@ mod Master { #[derive(Drop, starknet::Event)] struct MigrationQueued { old_address: ContractAddress, - new_address: ContractAddress, - hash: felt252 + new_address: ContractAddress } // @notice An event emitted whenever SBT is migrated @@ -211,7 +196,7 @@ mod Master { // // Getters // - fn get_contibutions_points(self: @ContractState, contributor: ContractAddress) -> Contribution { + fn get_contributions_points(self: @ContractState, contributor: ContractAddress) -> Contribution { self._contributions.read(contributor) } @@ -313,81 +298,35 @@ mod Master { loop { if (current_index == contributions.len()) { - break true; + break; } let new_contributions: MonthlyContribution = *contributions[current_index]; let contributor: ContractAddress = new_contributions.contributor; let old_contribution = self._contributions.read(contributor); - if (new_contributions.dev != 0) { - let mut contribution_data_dev = self._contributions_data.read((contributor,'dev')); - - contribution_data_dev.append(month_id); - contribution_data_dev.append(new_contributions.dev); - - self._contributions_data.write((contributor,'dev'),contribution_data_dev); - } - let new_dev_contribution = old_contribution.dev + new_contributions.dev; - - - - if (new_contributions.design != 0) { - let mut contribution_data_design = self._contributions_data.read((contributor,'design')); - - contribution_data_design.append(month_id); - contribution_data_design.append(new_contributions.design); - - self._contributions_data.write((contributor,'design'),contribution_data_design); - - } - let new_design_contribution = old_contribution.design + new_contributions.design; - - - if (new_contributions.problem_solving != 0) { - let mut contribution_data_problem_solving = self._contributions_data.read((contributor,'problem_solving')); - - contribution_data_problem_solving.append(month_id); - contribution_data_problem_solving.append(new_contributions.problem_solving); - - self._contributions_data.write((contributor,'problem_solving'),contribution_data_problem_solving); - - } - let new_problem_solving_contribution = old_contribution.problem_solving + new_contributions.problem_solving; - - - if (new_contributions.marcom != 0) { - let mut contribution_data_marcom = self._contributions_data.read((contributor,'marcom')); - - contribution_data_marcom.append(month_id); - contribution_data_marcom.append(new_contributions.marcom); - - self._contributions_data.write((contributor,'marcom'),contribution_data_marcom); - } - let new_marcom_contribution = old_contribution.marcom + new_contributions.marcom; - - - if (new_contributions.research != 0) { - let mut contribution_data_research = self._contributions_data.read((contributor,'research')); - - contribution_data_research.append(month_id); - contribution_data_research.append(new_contributions.research); - - self._contributions_data.write((contributor,'research'),contribution_data_research); - - } - let new_research_contribution = old_contribution.research + new_contributions.research; + let new_dev_contribution = InternalImpl::_update_guild_data(ref self, old_contribution.dev, new_contributions.dev, month_id, contributor, 'dev'); + let new_design_contribution = InternalImpl::_update_guild_data(ref self, old_contribution.design, new_contributions.design, month_id, contributor, 'design'); + let new_problem_solving_contribution = InternalImpl::_update_guild_data(ref self, old_contribution.problem_solving, new_contributions.problem_solving, month_id, contributor, 'problem_solving'); + let new_marcom_contribution = InternalImpl::_update_guild_data(ref self, old_contribution.marcom, new_contributions.marcom, month_id, contributor, 'marcom'); + let new_research_contribution = InternalImpl::_update_guild_data(ref self, old_contribution.research, new_contributions.research, month_id, contributor, 'research'); + dev_total_cum += new_contributions.dev; + design_total_cum += new_contributions.design; + problem_solving_total_cum += new_contributions.problem_solving; + marcom_total_cum += new_contributions.marcom; + research_total_cum += new_contributions.research; let updated_contribution = Contribution{dev: new_dev_contribution, design: new_design_contribution, problem_solving: new_problem_solving_contribution, marcom: new_marcom_contribution, research: new_research_contribution, last_timestamp: block_timestamp}; self._contributions.write(contributor, updated_contribution); - let total_monthy_contribution = TotalMonthlyContribution{dev: dev_total_cum, design: design_total_cum, problem_solving: problem_solving_total_cum, marcom: marcom_total_cum, research: research_total_cum}; - self._total_contribution.write(month_id, total_monthy_contribution); current_index += 1; self.emit(ContributionUpdated{update_id: id, contributor: contributor, month_id: month_id, points_earned: new_contributions}); }; + let total_monthy_contribution = TotalMonthlyContribution{dev: dev_total_cum, design: design_total_cum, problem_solving: problem_solving_total_cum, marcom: marcom_total_cum, research: research_total_cum}; + self._total_contribution.write(month_id, total_monthy_contribution); + id += 1; self._last_update_id.write(id); self._last_update_time.write(block_timestamp); @@ -402,7 +341,7 @@ mod Master { loop { if (current_index == old_addresses.len()) { - break true; + break; } InternalImpl::_migrate_points(ref self, *old_addresses[current_index], *new_addresses[current_index]); current_index += 1; @@ -418,10 +357,11 @@ mod Master { self._queued_migrations.write(migration_hash, true); - self.emit(MigrationQueued { old_address: caller, new_address: new_address, hash: migration_hash}); + self.emit(MigrationQueued { old_address: caller, new_address: new_address}); } + // @Notice the function has only_owner modifier to prevent user to use this function to tranfer SBT anytime. fn execute_migrate_points_initiated_by_holder(ref self: ContractState, old_address: ContractAddress, new_address: ContractAddress) { self._only_owner(); let migration_hash: felt252 = LegacyHash::hash(old_address.into(), new_address); @@ -429,8 +369,9 @@ mod Master { assert(is_queued == true, 'NOT_QUEUED'); - self._queued_migrations.write(migration_hash, false); InternalImpl::_migrate_points(ref self, old_address, new_address); + self._queued_migrations.write(migration_hash, false); + } @@ -444,6 +385,18 @@ mod Master { // Internals // + fn _update_guild_data(ref self: ContractState, old_guild_score: u32, new_contribution_score: u32, month_id: u32, contributor: ContractAddress, guild: felt252) -> u32 { + let new_guild_score = old_guild_score + new_contribution_score; + if(new_contribution_score != 0) { + let mut contribution_data = self._contributions_data.read((contributor, guild)); + contribution_data.append(month_id); + contribution_data.append(new_guild_score); + + self._contributions_data.write((contributor, guild), contribution_data); + } + (new_guild_score) + } + fn _migrate_points(ref self: ContractState, old_address: ContractAddress, new_address: ContractAddress) { let design_guild = self._design_guild_SBT.read(); @@ -464,42 +417,25 @@ mod Master { self._contributions.write(old_address, zero_contribution); self._contributions.write(new_address, contribution); - // updating contribution data - let dev_data = self._contributions_data.read((old_address, 'dev')); - let design_data = self._contributions_data.read((old_address, 'design')); - let problem_solving_data = self._contributions_data.read((old_address, 'problem_solving')); - let marcom_data = self._contributions_data.read((old_address, 'marcom')); - let research_data = self._contributions_data.read((old_address, 'research')); - - self._contributions_data.write((new_address, 'dev'), dev_data); - self._contributions_data.write((new_address, 'design'), design_data); - self._contributions_data.write((new_address, 'problem_solving'), problem_solving_data); - self._contributions_data.write((new_address, 'marcom'), marcom_data); - self._contributions_data.write((new_address, 'research'), research_data); - - self._contributions_data.write((old_address, 'dev'), ArrayTrait::new()); - self._contributions_data.write((old_address, 'design'), ArrayTrait::new()); - self._contributions_data.write((old_address, 'problem_solving'), ArrayTrait::new()); - self._contributions_data.write((old_address, 'marcom'), ArrayTrait::new()); - self._contributions_data.write((old_address, 'research'), ArrayTrait::new()); + // updating contribution data and transfering SBTs + InternalImpl::_update_contribution_data_and_migrate(ref self, old_address, new_address, 'dev', dev_guild); + InternalImpl::_update_contribution_data_and_migrate(ref self, old_address, new_address, 'design', design_guild); + InternalImpl::_update_contribution_data_and_migrate(ref self, old_address, new_address, 'problem_solving', problem_solver_guild); + InternalImpl::_update_contribution_data_and_migrate(ref self, old_address, new_address, 'marcom', marcom_guild); + InternalImpl::_update_contribution_data_and_migrate(ref self, old_address, new_address, 'research', research_guild); - let dev_guildDispatcher = IGuildDispatcher { contract_address: dev_guild }; - dev_guildDispatcher.migrate_sbt(old_address, new_address); - - let design_guildDispatcher = IGuildDispatcher { contract_address: design_guild }; - design_guildDispatcher.migrate_sbt(old_address, new_address); - - let problem_solver_guildDispatcher = IGuildDispatcher { contract_address: problem_solver_guild }; - problem_solver_guildDispatcher.migrate_sbt(old_address, new_address); + self.emit(Migrated{old_address: old_address, new_address: new_address}); - let marcom_guildDispatcher = IGuildDispatcher { contract_address: marcom_guild }; - marcom_guildDispatcher.migrate_sbt(old_address, new_address); + } - let research_guildDispatcher = IGuildDispatcher { contract_address: research_guild }; - research_guildDispatcher.migrate_sbt(old_address, new_address); + fn _update_contribution_data_and_migrate(ref self: ContractState, old_address: ContractAddress, new_address: ContractAddress, guild: felt252, guild_contract: ContractAddress) { + let guild_data = self._contributions_data.read((old_address, guild)); - self.emit(Migrated{old_address: old_address, new_address: new_address}); + self._contributions_data.write((new_address, guild), guild_data); + self._contributions_data.write((old_address, guild), ArrayTrait::new()); + let guildDispatcher = IGuildDispatcher { contract_address: guild_contract }; + guildDispatcher.migrate_sbt(old_address, new_address); } } diff --git a/tests/lib.cairo b/tests/lib.cairo new file mode 100644 index 0000000..fe83a70 --- /dev/null +++ b/tests/lib.cairo @@ -0,0 +1,5 @@ +mod test_deployment; +mod test_migrate_points; +mod test_mint; +mod test_update_contribution_points; +mod utils; \ No newline at end of file diff --git a/tests/test_deployment.cairo b/tests/test_deployment.cairo index cedeec7..ec44dfa 100644 --- a/tests/test_deployment.cairo +++ b/tests/test_deployment.cairo @@ -5,7 +5,7 @@ use starknet::ClassHash; use traits::TryInto; use option::OptionTrait; use snforge_std::{ declare, ContractClassTrait }; -use tests::utils::{ deployer_addr, user1}; +use tests::utils::{ deployer_addr, user1, URI}; #[starknet::interface] @@ -25,15 +25,6 @@ trait IGuildSBT { } -fn URI() -> Span { - let mut uri = ArrayTrait::new(); - - uri.append('api.jediswap/'); - uri.append('guildSBT/'); - uri.append('dev/'); - - uri.span() -} #[test] fn test_deployment_master_guildSBT() { @@ -79,8 +70,10 @@ fn test_deployment_master_guildSBT() { assert(symbol == 'JEDI-DEV', 'Invalid symbol'); let baseURI: Span = guildSBT_dispatcher.baseURI(); - // TODO: compare span with felt252 - // assert(baseURI == 'api.jediswap/guildSBT/dev/', 'Invalid base uri'); + assert(*baseURI[0] == 'api.jediswap/', 'Invlalid item 0'); + assert(*baseURI[1] == 'guildSBT/', 'Invlalid item 1'); + assert(*baseURI[2] == 'dev/', 'Invlalid item 2'); + assert(baseURI.len() == 3, 'should be 3'); let owner = guildSBT_dispatcher.owner(); assert(owner == deployer_addr(), 'Invalid Owner'); diff --git a/tests/test_migrate_points.cairo b/tests/test_migrate_points.cairo index c945f8e..b12f7b4 100644 --- a/tests/test_migrate_points.cairo +++ b/tests/test_migrate_points.cairo @@ -6,7 +6,7 @@ use traits::TryInto; use option::OptionTrait; use snforge_std::{ declare, ContractClassTrait, ContractClass, start_warp, start_prank, stop_prank, spy_events, SpyOn, EventSpy, EventFetcher, Event, EventAssertions }; -use tests::utils::{ deployer_addr, user1, user2, user3, user4}; +use tests::utils::{ deployer_addr, user1, user2, user3, user4, URI}; use contributor_SBT2_0::Master::MonthlyContribution; use contributor_SBT2_0::Master::Contribution; @@ -14,7 +14,7 @@ use contributor_SBT2_0::Master::Contribution; trait IMaster { fn get_last_update_id(self: @TContractState) -> u32; fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> u32; - fn get_contibutions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; + fn get_contributions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); fn migrate_points_initiated_by_DAO(ref self: TContractState, old_addresses: Array::, new_addresses: Array:: ); @@ -36,15 +36,7 @@ trait IGuildSBT { } -fn URI() -> Span { - let mut uri = ArrayTrait::new(); - uri.append('api.jediswap/'); - uri.append('guildSBT/'); - uri.append('dev/'); - - uri.span() -} fn deploy_contracts_and_initialise() -> (ContractAddress, ContractAddress, ContractAddress, ContractAddress, ContractAddress, ContractAddress) { let mut master_constructor_calldata = Default::default(); @@ -83,11 +75,10 @@ fn deploy_contracts_and_initialise() -> (ContractAddress, ContractAddress, Contr master_dispatcher.initialise(dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address); stop_prank(master_address); - - (master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address) } + fn update_contribution_and_minting_sbt(master_address: ContractAddress, dev_guildSBT_address: ContractAddress, design_guildSBT_address: ContractAddress, marcom_guildSBT_address: ContractAddress, problem_solving_guildSBT_address: ContractAddress, research_guildSBT_address: ContractAddress) -> (MonthlyContribution, MonthlyContribution) { let master_dispatcher = IMasterDispatcher { contract_address: master_address }; @@ -171,14 +162,14 @@ fn test_migrate_points_initiated_by_DAO() { // verifying points are successfully migrated - let user3_contribution: Contribution = master_dispatcher.get_contibutions_points(user3()); + let user3_contribution: Contribution = master_dispatcher.get_contributions_points(user3()); assert(user3_contribution.dev == user1_contribution.dev, ''); assert(user3_contribution.design == user1_contribution.design, ''); assert(user3_contribution.marcom == user1_contribution.marcom, ''); assert(user3_contribution.problem_solving == user1_contribution.problem_solving, ''); assert(user3_contribution.research == user1_contribution.research, ''); - let user4_contribution: Contribution = master_dispatcher.get_contibutions_points(user4()); + let user4_contribution: Contribution = master_dispatcher.get_contributions_points(user4()); assert(user4_contribution.dev == user2_contribution.dev, ''); assert(user4_contribution.design == user2_contribution.design, ''); assert(user4_contribution.marcom == user2_contribution.marcom, ''); @@ -187,14 +178,14 @@ fn test_migrate_points_initiated_by_DAO() { // verfying points of old addresses is resetted to zero - let user1_contribution_updated: Contribution = master_dispatcher.get_contibutions_points(user1()); + let user1_contribution_updated: Contribution = master_dispatcher.get_contributions_points(user1()); assert(user1_contribution_updated.dev == 0, ''); assert(user1_contribution_updated.design == 0, ''); assert(user1_contribution_updated.marcom == 0, ''); assert(user1_contribution_updated.problem_solving == 0, ''); assert(user1_contribution_updated.research == 0, ''); - let user2_contribution_updated: Contribution = master_dispatcher.get_contibutions_points(user2()); + let user2_contribution_updated: Contribution = master_dispatcher.get_contributions_points(user2()); assert(user2_contribution_updated.dev == 0, ''); assert(user2_contribution_updated.design == 0, ''); assert(user2_contribution_updated.marcom == 0, ''); @@ -234,6 +225,66 @@ fn test_migrate_points_initiated_by_DAO() { } +#[test] +#[should_panic(expected: ('Caller is not the owner', ))] +fn test_migrate_points_initiated_by_DAO_not_owner() { + let (master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address) = deploy_contracts_and_initialise(); + let (user1_contribution, user2_contribution) = update_contribution_and_minting_sbt(master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address); + + let master_dispatcher = IMasterDispatcher { contract_address: master_address }; + + // migrating user1 -> user3 and user2 -> user4 + let mut old_addresses = ArrayTrait::new(); + old_addresses.append(user1()); + old_addresses.append(user2()); + + let mut new_addresses = ArrayTrait::new(); + new_addresses.append(user3()); + new_addresses.append(user4()); + + let mut spy = spy_events(SpyOn::One(master_address)); + + start_prank(master_address, user1()); + master_dispatcher.migrate_points_initiated_by_DAO(old_addresses, new_addresses); + stop_prank(master_address); + + let mut event_data_1 = Default::default(); + Serde::serialize(@user1(), ref event_data_1); + Serde::serialize(@user3(), ref event_data_1); + spy.assert_emitted(@array![ + Event { from: master_address, name: 'Migrated', keys: array![], data: event_data_1 } + ]); + + let mut event_data_2 = Default::default(); + Serde::serialize(@user2(), ref event_data_2); + Serde::serialize(@user4(), ref event_data_2); + spy.assert_emitted(@array![ + Event { from: master_address, name: 'Migrated', keys: array![], data: event_data_2 } + ]); + +} + +#[test] +#[should_panic(expected: ('INVALID_INPUTS', ))] +fn test_migrate_points_initiated_by_DAO_length_mismatch() { + let (master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address) = deploy_contracts_and_initialise(); + let (user1_contribution, user2_contribution) = update_contribution_and_minting_sbt(master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address); + + let master_dispatcher = IMasterDispatcher { contract_address: master_address }; + + // migrating user1 -> user3 and user2 -> X (length mismatch) + let mut old_addresses = ArrayTrait::new(); + old_addresses.append(user1()); + old_addresses.append(user2()); + + let mut new_addresses = ArrayTrait::new(); + new_addresses.append(user3()); + + start_prank(master_address, deployer_addr()); + master_dispatcher.migrate_points_initiated_by_DAO(old_addresses, new_addresses); + stop_prank(master_address); +} + #[test] fn test_migrate_points_initiated_by_holder() { let (master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address) = deploy_contracts_and_initialise(); @@ -248,19 +299,34 @@ fn test_migrate_points_initiated_by_holder() { let user_1_dev_token_id = dev_guildSBT_dispatcher.wallet_of_owner(user1()); let user_1_design_token_id = design_guildSBT_dispatcher.wallet_of_owner(user1()); + let mut spy = spy_events(SpyOn::One(master_address)); + // initiating migration request start_prank(master_address, user1()); master_dispatcher.migrate_points_initiated_by_holder(user3()); stop_prank(master_address); + let mut event_data_1 = Default::default(); + Serde::serialize(@user1(), ref event_data_1); + Serde::serialize(@user3(), ref event_data_1); + spy.assert_emitted(@array![ + Event { from: master_address, name: 'MigrationQueued', keys: array![], data: event_data_1 } + ]); + // executing migration (by DAO) start_prank(master_address, deployer_addr()); master_dispatcher.execute_migrate_points_initiated_by_holder(user1(), user3()); stop_prank(master_address); + let mut event_data_2 = Default::default(); + Serde::serialize(@user1(), ref event_data_2); + Serde::serialize(@user3(), ref event_data_2); + spy.assert_emitted(@array![ + Event { from: master_address, name: 'Migrated', keys: array![], data: event_data_2 } + ]); // verifying points are successfully migrated - let user3_contribution: Contribution = master_dispatcher.get_contibutions_points(user3()); + let user3_contribution: Contribution = master_dispatcher.get_contributions_points(user3()); assert(user3_contribution.dev == user1_contribution.dev, ''); assert(user3_contribution.design == user1_contribution.design, ''); assert(user3_contribution.marcom == user1_contribution.marcom, ''); @@ -268,7 +334,7 @@ fn test_migrate_points_initiated_by_holder() { assert(user3_contribution.research == user1_contribution.research, ''); // verfying points of old addresses is resetted to zero - let user1_contribution_updated: Contribution = master_dispatcher.get_contibutions_points(user1()); + let user1_contribution_updated: Contribution = master_dispatcher.get_contributions_points(user1()); assert(user1_contribution_updated.dev == 0, ''); assert(user1_contribution_updated.design == 0, ''); assert(user1_contribution_updated.marcom == 0, ''); @@ -295,4 +361,18 @@ fn test_migrate_points_initiated_by_holder() { assert(user_3_design_token_id == user_1_design_token_id, ''); +} + +#[should_panic(expected: ('NOT_QUEUED', ))] +#[test] +fn test_execute_migrate_points_without_initiating() { + let (master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address) = deploy_contracts_and_initialise(); + let (user1_contribution, user2_contribution) = update_contribution_and_minting_sbt(master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address); + + let master_dispatcher = IMasterDispatcher { contract_address: master_address }; + + // executing migration (by DAO) without initiated by holder. + start_prank(master_address, deployer_addr()); + master_dispatcher.execute_migrate_points_initiated_by_holder(user1(), user3()); + stop_prank(master_address); } \ No newline at end of file diff --git a/tests/test_mint.cairo b/tests/test_mint.cairo index 9e63d20..816f1ff 100644 --- a/tests/test_mint.cairo +++ b/tests/test_mint.cairo @@ -6,7 +6,7 @@ use traits::TryInto; use option::OptionTrait; use snforge_std::{ declare, ContractClassTrait, ContractClass, start_warp, start_prank, stop_prank, spy_events, SpyOn, EventSpy, EventFetcher, Event, EventAssertions }; -use tests::utils::{ deployer_addr, user1, user2}; +use tests::utils::{ deployer_addr, user1, user2, URI}; use contributor_SBT2_0::Master::MonthlyContribution; #[starknet::interface] @@ -24,21 +24,13 @@ trait IGuildSBT { fn wallet_of_owner(self: @TContractState, account: ContractAddress) -> u256; fn get_contribution_tier(self: @TContractState, contributor: ContractAddress) -> u32; fn tokenURI(self: @TContractState, token_id: u256) -> Span; + fn get_next_token_id(self: @TContractState) -> u256; + fn safe_mint(ref self: TContractState, token_type: u8); } -fn URI() -> Span { - let mut uri = ArrayTrait::new(); - - uri.append('api.jediswap/'); - uri.append('guildSBT/'); - uri.append('dev/'); - - uri.span() -} - fn deploy_contracts() -> (ContractAddress, ContractAddress) { let mut master_constructor_calldata = Default::default(); Serde::serialize(@deployer_addr(), ref master_constructor_calldata); @@ -99,6 +91,8 @@ fn test_mint() { master_dispatcher.update_contibutions(092023, contributions); stop_prank(master_address); + let expected_token_id = guildSBT_dispatcher.get_next_token_id(); + start_prank(guildSBT_address, user1()); guildSBT_dispatcher.safe_mint(1); stop_prank(guildSBT_address); @@ -107,22 +101,33 @@ fn test_mint() { assert(new_balance == 1, 'invalid balance'); let user1_token_id = guildSBT_dispatcher.wallet_of_owner(user1()); + assert(user1_token_id == expected_token_id, 'Incorrect token id'); let tokenURI = guildSBT_dispatcher.tokenURI(user1_token_id); - // TODO: comapre span to string - // assert(tokenURI == 'api.jediswap/guildSBT/dev/21', 'invalid uri'); + assert(*tokenURI[0] == 'api.jediswap/', 'Invlalid item 0'); + assert(*tokenURI[1] == 'guildSBT/', 'Invlalid item 1'); + assert(*tokenURI[2] == 'dev/', 'Invlalid item 2'); + assert(*tokenURI[3] == '2', 'Invlalid tier (item 3)'); + assert(*tokenURI[4] == '1', 'Invlalid type (item 4)'); + assert(*tokenURI[5] == '.json', 'Invlalid item 5'); + assert(tokenURI.len() == 6, 'should be 6'); + + //verifying token id is updated + let updated_token_id = guildSBT_dispatcher.get_next_token_id(); + assert(updated_token_id == expected_token_id + 1, 'token id not updated'); } #[test] -#[should_panic(expected: ('ALREADY_MINTED', ))] +// #[should_panic(expected: ('ALREADY_MINTED', ))] fn test_mint_second_sbt() { let (master_address, guildSBT_address) = deploy_contracts(); let master_dispatcher = IMasterDispatcher { contract_address: master_address }; let guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: guildSBT_address }; + let safe_guildSBT_dispatcher = IGuildSBTSafeDispatcher { contract_address: guildSBT_address }; let balance = guildSBT_dispatcher.balance_of(user1()); - assert(balance == 0, 'invlaid initialisation'); + assert(balance == 0, 'invalid initialisation'); let mut contributions: Array = ArrayTrait::new(); contributions.append(MonthlyContribution{ contributor: user1(), dev: 240, design: 250, marcom: 20, problem_solving: 30, research: 10}); @@ -133,14 +138,19 @@ fn test_mint_second_sbt() { stop_prank(master_address); start_prank(guildSBT_address, user1()); - guildSBT_dispatcher.safe_mint(1); + safe_guildSBT_dispatcher.safe_mint(1); stop_prank(guildSBT_address); let new_balance = guildSBT_dispatcher.balance_of(user1()); assert(new_balance == 1, 'invalid balance'); start_prank(guildSBT_address, user1()); - guildSBT_dispatcher.safe_mint(1); + match safe_guildSBT_dispatcher.safe_mint(1) { + Result::Ok(_) => panic_with_felt252('shouldve panicked'), + Result::Err(panic_data) => { + assert(*panic_data.at(0) == 'ALREADY_MINTED', *panic_data.at(0)); + } + }; stop_prank(guildSBT_address); } \ No newline at end of file diff --git a/tests/test_update_contribution_points.cairo b/tests/test_update_contribution_points.cairo index 6079aa9..c0cdcb8 100644 --- a/tests/test_update_contribution_points.cairo +++ b/tests/test_update_contribution_points.cairo @@ -6,7 +6,7 @@ use traits::TryInto; use option::OptionTrait; use snforge_std::{ declare, ContractClassTrait, ContractClass, start_warp, start_prank, stop_prank, spy_events, SpyOn, EventSpy, EventFetcher, Event, EventAssertions }; -use tests::utils::{ deployer_addr, user1, user2}; +use tests::utils::{ deployer_addr, user1, user2, user3, user4, URI}; use contributor_SBT2_0::Master::MonthlyContribution; use contributor_SBT2_0::Master::Contribution; use contributor_SBT2_0::Master::TotalMonthlyContribution; @@ -15,8 +15,10 @@ use contributor_SBT2_0::Master::TotalMonthlyContribution; trait IMaster { fn get_last_update_id(self: @TContractState) -> u32; fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> u32; - fn get_contibutions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; - fn get_contributions_data(self: @TContractState, contributor: ContractAddress) -> Array; + fn get_contributions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; + fn get_contributions_data(self: @TContractState, contributor: ContractAddress, guild: felt252) -> Array; + fn get_total_contribution(self: @TContractState, month_id: u32) -> TotalMonthlyContribution; + fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); @@ -30,15 +32,6 @@ trait IGuildSBT { fn get_master(self: @TContractState) -> ContractAddress; } -fn URI() -> Span { - let mut uri = ArrayTrait::new(); - - uri.append('api.jediswap/'); - uri.append('guildSBT/'); - uri.append('dev/'); - - uri.span() -} fn deploy_contracts() -> (ContractAddress, ContractAddress) { let mut master_constructor_calldata = Default::default(); @@ -77,21 +70,124 @@ fn test_update_contribution_points() { let id1 = master_dispatcher.get_last_update_id(); assert (id1 == 0, 'Invalid id initialisation'); + let month_id: u32 = 092023; + + let user1_contribution = MonthlyContribution{ contributor: user1(), dev: 120, design: 250, marcom: 20, problem_solving: 0, research: 10}; + let user2_contribution = MonthlyContribution{ contributor: user2(), dev: 200, design: 30, marcom: 0, problem_solving: 0, research: 50}; + let user3_contribution = MonthlyContribution{ contributor: user3(), dev: 30, design: 100, marcom: 0, problem_solving: 0, research: 50}; + let user4_contribution = MonthlyContribution{ contributor: user4(), dev: 1500, design: 0, marcom: 25, problem_solving: 0, research: 100}; let mut contributions: Array = ArrayTrait::new(); - contributions.append(MonthlyContribution{ contributor: user1(), dev: 120, design: 250, marcom: 20, problem_solving: 30, research: 10}); - contributions.append(MonthlyContribution{ contributor: user2(), dev: 200, design: 30, marcom: 0, problem_solving: 0, research: 50}); + contributions.append(user1_contribution); + contributions.append(user2_contribution); + contributions.append(user3_contribution); + contributions.append(user4_contribution); + + let mut spy = spy_events(SpyOn::One(master_address)); start_prank(master_address, deployer_addr()); - master_dispatcher.update_contibutions(092023, contributions); + master_dispatcher.update_contibutions(month_id, contributions); stop_prank(master_address); + let mut user1_event_data = Default::default(); + Serde::serialize(@id1, ref user1_event_data); + Serde::serialize(@user1(), ref user1_event_data); + Serde::serialize(@month_id, ref user1_event_data); + Serde::serialize(@user1_contribution, ref user1_event_data); + spy.assert_emitted(@array![ + Event { from: master_address, name: 'ContributionUpdated', keys: array![], data: user1_event_data } + ]); + + let mut user2_event_data = Default::default(); + Serde::serialize(@id1, ref user2_event_data); + Serde::serialize(@user2(), ref user2_event_data); + Serde::serialize(@month_id, ref user2_event_data); + Serde::serialize(@user2_contribution, ref user2_event_data); + spy.assert_emitted(@array![ + Event { from: master_address, name: 'ContributionUpdated', keys: array![], data: user2_event_data } + ]); + + let mut user3_event_data = Default::default(); + Serde::serialize(@id1, ref user3_event_data); + Serde::serialize(@user3(), ref user3_event_data); + Serde::serialize(@month_id, ref user3_event_data); + Serde::serialize(@user3_contribution, ref user3_event_data); + spy.assert_emitted(@array![ + Event { from: master_address, name: 'ContributionUpdated', keys: array![], data: user3_event_data } + ]); + + let mut user4_event_data = Default::default(); + Serde::serialize(@id1, ref user4_event_data); + Serde::serialize(@user4(), ref user4_event_data); + Serde::serialize(@month_id, ref user4_event_data); + Serde::serialize(@user4_contribution, ref user4_event_data); + spy.assert_emitted(@array![ + Event { from: master_address, name: 'ContributionUpdated', keys: array![], data: user4_event_data } + ]); + let id2 = master_dispatcher.get_last_update_id(); assert (id2 == 1, 'invalid id'); - let tier1 = guildSBT_dispatcher.get_contribution_tier(user1()); - assert (tier1 == 1, 'invalid tier1'); + // verifying points for user 1 is updated + let mut points = master_dispatcher.get_contributions_points(user1()); + assert(points.dev == 120, 'invalid dev point'); + assert(points.design == 250, 'invalid design point'); + assert(points.marcom == 20, 'invalid marcom point'); + assert(points.problem_solving == 0, 'invalid problem solving point'); + assert(points.research == 10, 'invalid research point'); + + // verifying points for user 4 is updated + points = master_dispatcher.get_contributions_points(user4()); + assert(points.dev == 1500, 'invalid dev point'); + assert(points.design == 0, 'invalid design point'); + assert(points.marcom == 25, 'invalid marcom point'); + assert(points.problem_solving == 0, 'invalid problem solving point'); + assert(points.research == 100, 'invalid research point'); + + // verifying contribution data(Montly) is updated + let mut dev_data = master_dispatcher.get_contributions_data(user1(), 'dev'); + assert(dev_data.len() == 2, 'invalid length'); + assert(*dev_data[0] == month_id, 'invalid month id'); + assert(*dev_data[1] == 120, 'invalid month points'); + + let mut design_data = master_dispatcher.get_contributions_data(user1(), 'design'); + assert(design_data.len() == 2, 'invalid length'); + assert(*design_data[0] == month_id, 'invalid month id'); + assert(*design_data[1] == 250, 'invalid month points'); + + let mut marcom_data = master_dispatcher.get_contributions_data(user1(), 'marcom'); + assert(marcom_data.len() == 2, 'invalid length'); + assert(*marcom_data[0] == month_id, 'invalid month id'); + assert(*marcom_data[1] == 20, 'invalid month points'); + + let mut problem_solving_data = master_dispatcher.get_contributions_data(user1(), 'problem_solving'); + assert(problem_solving_data.len() == 0, 'invalid length'); + + let mut research_data = master_dispatcher.get_contributions_data(user1(), 'research'); + assert(research_data.len() == 2, 'invalid length'); + assert(*research_data[0] == month_id, 'invalid month id'); + assert(*research_data[1] == 10, 'invalid month points'); + + + // verifying tier for each levels + let tier_user1 = guildSBT_dispatcher.get_contribution_tier(user1()); + assert (tier_user1 == 1, 'invalid tier_user1'); + + let tier_user2 = guildSBT_dispatcher.get_contribution_tier(user2()); + assert (tier_user2 == 2, 'invalid tier_user2'); + + let tier_user3 = guildSBT_dispatcher.get_contribution_tier(user3()); + assert (tier_user3 == 0, 'invalid tier_user3'); + + let tier_user4 = guildSBT_dispatcher.get_contribution_tier(user4()); + assert (tier_user4 == 4, 'invalid tier_user4'); + + // verifying total monthly contribution + let total_contribution_point = master_dispatcher.get_total_contribution(month_id); + assert(total_contribution_point.dev == 1850, 'incorrect total dev'); + assert(total_contribution_point.design == 380, 'incorrect total design'); + assert(total_contribution_point.marcom == 45, 'incorrect total marcom'); + assert(total_contribution_point.problem_solving == 0, 'incorrect total problem solving'); + assert(total_contribution_point.research == 210, 'incorrect total research'); - let tier2 = guildSBT_dispatcher.get_contribution_tier(user2()); - assert (tier2 == 2, 'invalid tier2'); } \ No newline at end of file diff --git a/tests/utils.cairo b/tests/utils.cairo index 78d0b5b..41838e7 100644 --- a/tests/utils.cairo +++ b/tests/utils.cairo @@ -24,4 +24,14 @@ fn user3() -> ContractAddress { fn user4() -> ContractAddress { contract_address_try_from_felt252('user4').unwrap() +} + +fn URI() -> Span { + let mut uri = ArrayTrait::new(); + + uri.append('api.jediswap/'); + uri.append('guildSBT/'); + uri.append('dev/'); + + uri.span() } \ No newline at end of file From 7ff85157a0f92c781a7c06bdcde1aa0f1acf1096 Mon Sep 17 00:00:00 2001 From: yashm001 Date: Tue, 24 Oct 2023 12:40:55 +0530 Subject: [PATCH 17/22] review changes --- src/GuildSBT.cairo | 8 +++--- src/Master.cairo | 7 +++--- src/lib.cairo | 4 +-- tests/test_migrate_points.cairo | 28 +++++++-------------- tests/test_mint.cairo | 3 +-- tests/test_update_contribution_points.cairo | 6 ++--- 6 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/GuildSBT.cairo b/src/GuildSBT.cairo index 7ea2631..0335b63 100644 --- a/src/GuildSBT.cairo +++ b/src/GuildSBT.cairo @@ -41,7 +41,7 @@ trait IGuildSBT { mod GuildSBT { use option::OptionTrait; - use traits::{Into, TryInto, Default, Felt252DictValue}; + // use traits::Into; use array::{SpanSerde, ArrayTrait}; use clone::Clone; use array::SpanTrait; @@ -278,7 +278,7 @@ mod GuildSBT { let mut number: u128 = number_in.try_into().unwrap(); let mut tmpArray: Array = ArrayTrait::new(); loop { - if number == 0 { + if (number == 0.try_into().unwrap()) { break; } let digit: u128 = number % 10; @@ -286,11 +286,11 @@ mod GuildSBT { tmpArray.append(digit.into() + 48); }; let mut i: u32 = tmpArray.len(); - if i == 0 { // deal with 0 case + if (i == 0.try_into().unwrap()) { // deal with 0 case uri.append(48); } loop { - if i == 0 { + if i == 0.try_into().unwrap() { break; } i -= 1; diff --git a/src/Master.cairo b/src/Master.cairo index 4899c58..d572fbb 100644 --- a/src/Master.cairo +++ b/src/Master.cairo @@ -414,9 +414,6 @@ mod Master { last_timestamp: 0_u64 }; - self._contributions.write(old_address, zero_contribution); - self._contributions.write(new_address, contribution); - // updating contribution data and transfering SBTs InternalImpl::_update_contribution_data_and_migrate(ref self, old_address, new_address, 'dev', dev_guild); InternalImpl::_update_contribution_data_and_migrate(ref self, old_address, new_address, 'design', design_guild); @@ -424,6 +421,9 @@ mod Master { InternalImpl::_update_contribution_data_and_migrate(ref self, old_address, new_address, 'marcom', marcom_guild); InternalImpl::_update_contribution_data_and_migrate(ref self, old_address, new_address, 'research', research_guild); + self._contributions.write(old_address, zero_contribution); + self._contributions.write(new_address, contribution); + self.emit(Migrated{old_address: old_address, new_address: new_address}); } @@ -441,7 +441,6 @@ mod Master { } - #[generate_trait] impl ModifierImpl of ModifierTrait { fn _only_owner(self: @ContractState) { diff --git a/src/lib.cairo b/src/lib.cairo index e1952b8..297dcc2 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -1,5 +1,5 @@ -mod Master; +mod master; mod access; mod storage; -mod GuildSBT; +mod guildSBT; mod array; \ No newline at end of file diff --git a/tests/test_migrate_points.cairo b/tests/test_migrate_points.cairo index b12f7b4..c3950a8 100644 --- a/tests/test_migrate_points.cairo +++ b/tests/test_migrate_points.cairo @@ -7,8 +7,8 @@ use option::OptionTrait; use snforge_std::{ declare, ContractClassTrait, ContractClass, start_warp, start_prank, stop_prank, spy_events, SpyOn, EventSpy, EventFetcher, Event, EventAssertions }; use tests::utils::{ deployer_addr, user1, user2, user3, user4, URI}; -use contributor_SBT2_0::Master::MonthlyContribution; -use contributor_SBT2_0::Master::Contribution; +use contributor_SBT2_0::master::MonthlyContribution; +use contributor_SBT2_0::master::Contribution; #[starknet::interface] trait IMaster { @@ -226,12 +226,11 @@ fn test_migrate_points_initiated_by_DAO() { } #[test] -#[should_panic(expected: ('Caller is not the owner', ))] fn test_migrate_points_initiated_by_DAO_not_owner() { let (master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address) = deploy_contracts_and_initialise(); let (user1_contribution, user2_contribution) = update_contribution_and_minting_sbt(master_address, dev_guildSBT_address, design_guildSBT_address, marcom_guildSBT_address, problem_solving_guildSBT_address, research_guildSBT_address); - let master_dispatcher = IMasterDispatcher { contract_address: master_address }; + let safe_master_dispatcher = IMasterSafeDispatcher { contract_address: master_address }; // migrating user1 -> user3 and user2 -> user4 let mut old_addresses = ArrayTrait::new(); @@ -245,23 +244,14 @@ fn test_migrate_points_initiated_by_DAO_not_owner() { let mut spy = spy_events(SpyOn::One(master_address)); start_prank(master_address, user1()); - master_dispatcher.migrate_points_initiated_by_DAO(old_addresses, new_addresses); + match safe_master_dispatcher.migrate_points_initiated_by_DAO(old_addresses, new_addresses) { + Result::Ok(_) => panic_with_felt252('shouldve panicked'), + Result::Err(panic_data) => { + assert(*panic_data.at(0) == 'Caller is not the owner', *panic_data.at(0)); + } + }; stop_prank(master_address); - let mut event_data_1 = Default::default(); - Serde::serialize(@user1(), ref event_data_1); - Serde::serialize(@user3(), ref event_data_1); - spy.assert_emitted(@array![ - Event { from: master_address, name: 'Migrated', keys: array![], data: event_data_1 } - ]); - - let mut event_data_2 = Default::default(); - Serde::serialize(@user2(), ref event_data_2); - Serde::serialize(@user4(), ref event_data_2); - spy.assert_emitted(@array![ - Event { from: master_address, name: 'Migrated', keys: array![], data: event_data_2 } - ]); - } #[test] diff --git a/tests/test_mint.cairo b/tests/test_mint.cairo index 816f1ff..a32f990 100644 --- a/tests/test_mint.cairo +++ b/tests/test_mint.cairo @@ -7,7 +7,7 @@ use option::OptionTrait; use snforge_std::{ declare, ContractClassTrait, ContractClass, start_warp, start_prank, stop_prank, spy_events, SpyOn, EventSpy, EventFetcher, Event, EventAssertions }; use tests::utils::{ deployer_addr, user1, user2, URI}; -use contributor_SBT2_0::Master::MonthlyContribution; +use contributor_SBT2_0::master::MonthlyContribution; #[starknet::interface] trait IMaster { @@ -119,7 +119,6 @@ fn test_mint() { } #[test] -// #[should_panic(expected: ('ALREADY_MINTED', ))] fn test_mint_second_sbt() { let (master_address, guildSBT_address) = deploy_contracts(); let master_dispatcher = IMasterDispatcher { contract_address: master_address }; diff --git a/tests/test_update_contribution_points.cairo b/tests/test_update_contribution_points.cairo index c0cdcb8..3a6bfe9 100644 --- a/tests/test_update_contribution_points.cairo +++ b/tests/test_update_contribution_points.cairo @@ -7,9 +7,9 @@ use option::OptionTrait; use snforge_std::{ declare, ContractClassTrait, ContractClass, start_warp, start_prank, stop_prank, spy_events, SpyOn, EventSpy, EventFetcher, Event, EventAssertions }; use tests::utils::{ deployer_addr, user1, user2, user3, user4, URI}; -use contributor_SBT2_0::Master::MonthlyContribution; -use contributor_SBT2_0::Master::Contribution; -use contributor_SBT2_0::Master::TotalMonthlyContribution; +use contributor_SBT2_0::master::MonthlyContribution; +use contributor_SBT2_0::master::Contribution; +use contributor_SBT2_0::master::TotalMonthlyContribution; #[starknet::interface] trait IMaster { From 7e81d6051ec029ae939aed76b8eefe0469731558 Mon Sep 17 00:00:00 2001 From: yashm001 <43926091+yashm001@users.noreply.github.com> Date: Tue, 24 Oct 2023 12:41:50 +0530 Subject: [PATCH 18/22] Delete target directory --- target/CACHEDIR.TAG | 3 --- target/dev/contributor_SBT2_0.starknet_artifacts.json | 1 - target/dev/contributor_SBT2_0_Master.casm.json | 1 - target/dev/contributor_SBT2_0_Master.sierra.json | 1 - 4 files changed, 6 deletions(-) delete mode 100644 target/CACHEDIR.TAG delete mode 100644 target/dev/contributor_SBT2_0.starknet_artifacts.json delete mode 100644 target/dev/contributor_SBT2_0_Master.casm.json delete mode 100644 target/dev/contributor_SBT2_0_Master.sierra.json diff --git a/target/CACHEDIR.TAG b/target/CACHEDIR.TAG deleted file mode 100644 index e95ca71..0000000 --- a/target/CACHEDIR.TAG +++ /dev/null @@ -1,3 +0,0 @@ -Signature: 8a477f597d28d172789f06886806bc55 -# This file is a cache directory tag created by scarb. -# For information about cache directory tags see https://bford.info/cachedir/ diff --git a/target/dev/contributor_SBT2_0.starknet_artifacts.json b/target/dev/contributor_SBT2_0.starknet_artifacts.json deleted file mode 100644 index 5e6b3a1..0000000 --- a/target/dev/contributor_SBT2_0.starknet_artifacts.json +++ /dev/null @@ -1 +0,0 @@ -{"version":1,"contracts":[{"id":"0af7eom6gavt2","package_name":"contributor_SBT2_0","contract_name":"Master","artifacts":{"sierra":"contributor_SBT2_0_Master.sierra.json","casm":"contributor_SBT2_0_Master.casm.json"}}]} \ No newline at end of file diff --git a/target/dev/contributor_SBT2_0_Master.casm.json b/target/dev/contributor_SBT2_0_Master.casm.json deleted file mode 100644 index 4d920ac..0000000 --- a/target/dev/contributor_SBT2_0_Master.casm.json +++ /dev/null @@ -1 +0,0 @@ -{"prime":"0x800000000000011000000000000000000000000000000000000000000000001","compiler_version":"2.2.0","bytecode":["0xa0680017fff8000","0x7","0x482680017ffa8000","0x100000000000000000000000000000000","0x400280007ff97fff","0x10780017fff7fff","0x49","0x4825800180007ffa","0x0","0x400280007ff97fff","0x48297ffc80007ffd","0x482680017ff98000","0x1","0x4824800180007ffe","0x0","0x20680017fff7fff","0x4","0x10780017fff7fff","0x10","0x40780017fff7fff","0x1","0x480680017fff8000","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x400080007ffe7fff","0x48127ffc7fff8000","0x48127ff97fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x1104800180018000","0x3d","0x482480017fff8000","0x3c","0x480080007fff8000","0xa0680017fff8000","0x9","0x4824800180007ff7","0x0","0x482480017fff8000","0x100000000000000000000000000000000","0x400080007ff77fff","0x10780017fff7fff","0x12","0x4824800180007ff7","0x0","0x400080007ff87fff","0x1104800180018000","0x2b","0x40780017fff7fff","0x1","0x482480017ff58000","0x1","0x48127ffb7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x0","0x48127ffb7fff8000","0x48127ffa7fff8000","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482480017ff58000","0x1","0x48127ff27fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x40780017fff7fff","0x1","0x480680017fff8000","0x4f7574206f6620676173","0x400080007ffe7fff","0x482680017ff98000","0x1","0x480a7ffa7fff8000","0x480a7ffb7fff8000","0x480680017fff8000","0x1","0x48127ffa7fff8000","0x482480017ff98000","0x1","0x208b7fff7fff7ffe","0x208b7fff7fff7ffe"],"hints":[[0,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"FP","offset":-6}},"dst":{"register":"AP","offset":0}}}]],[19,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[38,[{"TestLessThanOrEqual":{"lhs":{"Immediate":"0x0"},"rhs":{"Deref":{"register":"AP","offset":-8}},"dst":{"register":"AP","offset":0}}}]],[52,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[63,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]],[78,[{"AllocSegment":{"dst":{"register":"AP","offset":0}}}]]],"pythonic_hints":[[0,["memory[ap + 0] = 0 <= memory[fp + -6]"]],[19,["memory[ap + 0] = segments.add()"]],[38,["memory[ap + 0] = 0 <= memory[ap + -8]"]],[52,["memory[ap + 0] = segments.add()"]],[63,["memory[ap + 0] = segments.add()"]],[78,["memory[ap + 0] = segments.add()"]]],"entry_points_by_type":{"EXTERNAL":[],"L1_HANDLER":[],"CONSTRUCTOR":[{"selector":"0x28ffe4ff0f226a9107253e17a904099aa4f63a02a5621de0576e5aa71bc5194","offset":0,"builtins":["range_check"]}]}} \ No newline at end of file diff --git a/target/dev/contributor_SBT2_0_Master.sierra.json b/target/dev/contributor_SBT2_0_Master.sierra.json deleted file mode 100644 index 2650ec5..0000000 --- a/target/dev/contributor_SBT2_0_Master.sierra.json +++ /dev/null @@ -1 +0,0 @@ -{"sierra_program":["0x1","0x3","0x0","0x2","0x2","0x0","0x75","0x8b","0x11","0x52616e6765436865636b","0x800000000000000100000000000000000000000000000000","0x4172726179","0x800000000000000300000000000000000000000000000001","0x1","0xe","0x536e617073686f74","0x800000000000000700000000000000000000000000000001","0x537472756374","0x800000000000000700000000000000000000000000000002","0x0","0x1baeba72e79e9db2587cf44fedb2f3700b2075a5e8e39a562584862c4b71f62","0x2","0x2ee1e2b1b89f8c495f200e4956278a4d47395fe262f27b52e5865c9524c08c3","0x3","0x800000000000000f00000000000000000000000000000001","0x3bc084d3c7617fdd6c5518880820319a137a8a7b477b020610b85a6219edb83","0x1835d756ce4c2f042b51de151fbb313a64e1d594b276e47db4ef4ee71c80f50","0x800000000000000f00000000000000000000000000000003","0x2d78d0f1a11ade2f938c53bc8c2a5f956d376203245eccaf880bf121d4764cf","0x6","0x7","0x4275696c74696e436f737473","0x800000000000000700000000000000000000000000000000","0x53797374656d","0x16a4c8d7c05909052238a862d8cc3e7975bf05a07b3a69c6b28951083a6d672","0x800000000000000300000000000000000000000000000003","0xb","0x456e756d","0x9931c641b913035ae674b400b61a51476d506bbe8bba2ff8a6272790aba9e6","0x4","0xc","0x66656c74323532","0x753332","0x4761734275696c74696e","0x29","0x7265766f6b655f61705f747261636b696e67","0x77697468647261775f676173","0x6272616e63685f616c69676e","0x7374727563745f6465636f6e737472756374","0x61727261795f6c656e","0x736e617073686f745f74616b65","0xf","0x64726f70","0x7533325f636f6e7374","0x72656e616d65","0x73746f72655f74656d70","0x7533325f6571","0x61727261795f6e6577","0x66656c743235325f636f6e7374","0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473","0x61727261795f617070656e64","0x7374727563745f636f6e737472756374","0x656e756d5f696e6974","0xd","0x10","0xa","0x6765745f6275696c74696e5f636f737473","0x9","0x77697468647261775f6761735f616c6c","0x8","0x66756e6374696f6e5f63616c6c","0x5","0x4f7574206f6620676173","0x52","0xffffffffffffffff","0x40","0x1c","0x12","0x13","0x14","0x15","0x16","0x17","0x18","0x19","0x1a","0x1b","0x1d","0x33","0x1e","0x1f","0x20","0x21","0x22","0x25","0x23","0x24","0x26","0x27","0x28","0x2a","0x2b","0x2c","0x2d","0x2e","0x2f","0x30","0x31","0x32","0x34","0x35","0x36","0x37","0x38","0x39","0x3a","0x3b","0x3c","0x3d","0x3e","0x3f","0x41","0x42","0x43","0x4e","0x379","0x110b10090e0b10090f050e0b0a090d050c0b0a090505080706050403020100","0xb1b1d05051c050e0b1b091a0b10090219181716051505140b1309120b1009","0x2c2b05052a060505290f0505280b270b260b252402231822182120051f051e","0x52f330d0532060505310b300b05052f2b05052f2b05052e0b0d052d2b0505","0x5052f3905052f3805052f050d37050d36200505351c050535060505340605","0x52c050f053e3d05052f3d05053516050535150505350b3c3b05052f0b3a37","0xd05320b0d37050d361f0505350f0505350505052c0505052a3f05052c3d05","0x433f1f0d420d050b0d050b0b42050b0b0b413f05052f3f0505350f05052c40","0x42053b053f0b3b0542053d051f0b3d0542050f050f0b0b42050b0d0b16150d","0x53705150b06370d420520053f0b200542050b160b0b42053905150b1c390d","0x51f05390b2b0542052b053b0b3805420506053d0b2b0542051c053d0b0b42","0x42050b370b000542050b200b0b42050b0d0b0b440b420d382b0d1c0b1f0542","0x46470d000b470542050b380b4605420545000d2b0b450542054505060b4505","0x5470b4b0542053f05460b4a0542051f05390b490542054805450b48054205","0x542050b490b0b42050b0d0b4d4c4b4a1f054d0542054905480b4c0542050d","0xb0b42050b0d0b52510d504f440d420d4e3f1f0f4b0b4e0542054e054a0b4e","0xb560542055505440b5505420554530d4e0b540542050b4d0b530542050b4c","0x530b590542050b200b0b42055805520b0b42055705510b58570d420556054f","0xb5c0542052405570b240542055b05550b0b42055a05540b5b5a0d42055905","0x600542050d05470b5f0542054f05460b5e0542054405390b5d0542055c0558","0x50b560b620542050b200b0b42050b0d0b61605f5e1f05610542055d05480b","0x500d000b500542050b380b6405420563620d2b0b630542056305060b630542","0x470b680542055205460b670542055105390b660542056505450b6505420564","0x50f05590b0b42050b0d0b6a6968671f056a0542056605480b690542050d05","0x42056c6b0d2b0b6c0542056c05060b6c0542050b560b6b0542050b200b0b42","0x51505390b700542056f05450b6f0542056d6e0d000b6e0542050b380b6d05","0x71431f05730542057005480b720542050d05470b710542051605460b430542","0x1f0d0f0d0d050f05420505055b0b0d0542050b05440b050542050b5a0b7372","0x740b3f3d0d3d050b0f0d050b3739380b1f0f39380b"],"sierra_program_debug_info":{"type_names":[[0,"RangeCheck"],[1,"Array"],[2,"Snapshot>"],[3,"core::array::Span::"],[4,"Tuple>"],[5,"Unit"],[6,"contributor_SBT2_0::Master::Master::_contributions::ContractMemberState"],[7,"contributor_SBT2_0::Master::Master::_total_contribution::ContractMemberState"],[8,"contributor_SBT2_0::Master::Master::ContractState"],[9,"BuiltinCosts"],[10,"System"],[11,"core::panics::Panic"],[12,"Tuple>"],[13,"core::panics::PanicResult::<(core::array::Span::,)>"],[14,"felt252"],[15,"u32"],[16,"GasBuiltin"]],"libfunc_names":[[0,"revoke_ap_tracking"],[1,"withdraw_gas"],[2,"branch_align"],[3,"struct_deconstruct>"],[4,"array_len"],[5,"snapshot_take"],[6,"drop"],[7,"u32_const<0>"],[8,"rename"],[9,"store_temp"],[10,"store_temp"],[11,"u32_eq"],[12,"array_new"],[13,"felt252_const<7733229381460288120802334208475838166080759535023995805565484692595>"],[14,"store_temp"],[15,"array_append"],[16,"struct_construct"],[17,"struct_construct>>"],[18,"enum_init,)>, 1>"],[19,"store_temp"],[20,"store_temp"],[21,"store_temp,)>>"],[22,"get_builtin_costs"],[23,"store_temp"],[24,"withdraw_gas_all"],[25,"struct_construct"],[26,"struct_construct"],[27,"struct_construct"],[28,"store_temp"],[29,"function_call"],[30,"drop"],[31,"drop"],[32,"snapshot_take>"],[33,"drop>"],[34,"struct_construct>"],[35,"struct_construct>>"],[36,"enum_init,)>, 0>"],[37,"felt252_const<375233589013918064796019>"],[38,"drop>"],[39,"struct_construct"],[40,"store_temp"]],"user_func_names":[[0,"contributor_SBT2_0::Master::Master::__wrapper_constructor"],[1,"contributor_SBT2_0::Master::Master::constructor"]]},"contract_class_version":"0.1.0","entry_points_by_type":{"EXTERNAL":[],"L1_HANDLER":[],"CONSTRUCTOR":[{"selector":"0x28ffe4ff0f226a9107253e17a904099aa4f63a02a5621de0576e5aa71bc5194","function_idx":0}]},"abi":[{"type":"impl","name":"Master","interface_name":"contributor_SBT2_0::Master::IMaster"},{"type":"interface","name":"contributor_SBT2_0::Master::IMaster","items":[]},{"type":"constructor","name":"constructor","inputs":[]},{"type":"event","name":"contributor_SBT2_0::Master::Master::Event","kind":"enum","variants":[]}]} \ No newline at end of file From 513f948bd0dcaa2ae0c4c1e9799aa59398c45354 Mon Sep 17 00:00:00 2001 From: yashm001 Date: Sun, 29 Oct 2023 12:43:40 +0530 Subject: [PATCH 19/22] generalised get points functions --- src/GuildSBT.cairo | 10 +-- src/Master.cairo | 73 +++++++++++++-------- src/lib.cairo | 3 +- tests/test_migrate_points.cairo | 1 - tests/test_mint.cairo | 1 - tests/test_update_contribution_points.cairo | 1 - 6 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/GuildSBT.cairo b/src/GuildSBT.cairo index 0335b63..7e40ab8 100644 --- a/src/GuildSBT.cairo +++ b/src/GuildSBT.cairo @@ -9,7 +9,7 @@ use array::Array; #[starknet::interface] trait IMaster { - fn get_dev_points(self: @T, contributor: ContractAddress) -> u32; + fn get_guild_points(self: @T, contributor: ContractAddress, guild: felt252) -> u32; } // @@ -114,7 +114,7 @@ mod GuildSBT { let master = self._master.read(); let masterDispatcher = IMasterDispatcher { contract_address: master }; // @notice this is a sample SBT contract for dev guild, update the next line before deploying other guild - let points = masterDispatcher.get_dev_points(owner); + let points = masterDispatcher.get_guild_points(owner, 'dev'); let token_type = self._token_type.read(owner); let tier = InternalImpl::_get_contribution_tier(self, points); @@ -129,7 +129,7 @@ mod GuildSBT { let master = self._master.read(); let masterDispatcher = IMasterDispatcher { contract_address: master }; // @notice this is a sample SBT contract for dev guild, update the next line before deploying other guild - let points = masterDispatcher.get_dev_points(contributor); + let points = masterDispatcher.get_guild_points(contributor, 'dev'); let token_type = self._token_type.read(contributor); let tier = InternalImpl::_get_contribution_tier(self, points); @@ -151,7 +151,7 @@ mod GuildSBT { fn get_contribution_tier(self: @ContractState, contributor: ContractAddress) -> u32 { let master = self._master.read(); let masterDispatcher = IMasterDispatcher { contract_address: master }; - let points = masterDispatcher.get_dev_points(contributor); + let points = masterDispatcher.get_guild_points(contributor, 'dev'); InternalImpl::_get_contribution_tier(self, points) } @@ -201,7 +201,7 @@ mod GuildSBT { let master = self._master.read(); let masterDispatcher = IMasterDispatcher { contract_address: master }; - let points = masterDispatcher.get_dev_points(account); + let points = masterDispatcher.get_guild_points(account, 'dev'); let tier = InternalImpl::_get_contribution_tier(@self, points); assert (tier != 0, 'NOT_ENOUGH_POINTS'); diff --git a/src/Master.cairo b/src/Master.cairo index d572fbb..8a77b6b 100644 --- a/src/Master.cairo +++ b/src/Master.cairo @@ -73,11 +73,7 @@ trait IGuild { trait IMaster { // view functions fn get_contributions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; - fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> u32; - fn get_design_points(self: @TContractState, contributor: ContractAddress) -> u32; - fn get_problem_solving_points(self: @TContractState, contributor: ContractAddress) -> u32; - fn get_marcom_points(self: @TContractState, contributor: ContractAddress) -> u32; - fn get_research_points(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_guild_points(self: @TContractState, contributor: ContractAddress, guild: felt252) -> u32; fn get_last_update_id(self: @TContractState) -> u32; fn get_last_update_time(self: @TContractState) -> u64; fn get_migartion_queued_state(self: @TContractState, hash: felt252 ) -> bool; @@ -88,6 +84,8 @@ trait IMaster { fn get_research_guild_SBT(self: @TContractState) -> ContractAddress; fn get_total_contribution(self: @TContractState, month_id: u32) -> TotalMonthlyContribution; fn get_contributions_data(self: @TContractState, contributor: ContractAddress, guild: felt252) -> Array; + fn get_guild_total_contribution(self: @TContractState, month_id: u32, guild: felt252) -> u32; + // external functions fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); @@ -204,33 +202,50 @@ mod Master { self._total_contribution.read(month_id) } - fn get_contributions_data(self: @ContractState, contributor: ContractAddress, guild: felt252) -> Array { - self._contributions_data.read((contributor, guild)) - } - - fn get_dev_points(self: @ContractState, contributor: ContractAddress) -> u32 { - let contribution: Contribution = self._contributions.read(contributor); - contribution.dev - } - - fn get_design_points(self: @ContractState, contributor: ContractAddress) -> u32 { - let contribution = self._contributions.read(contributor); - contribution.design - } - - fn get_problem_solving_points(self: @ContractState, contributor: ContractAddress) -> u32 { - let contribution = self._contributions.read(contributor); - contribution.problem_solving + fn get_guild_total_contribution(self: @ContractState, month_id: u32, guild: felt252) -> u32 { + if(guild == 'dev') { + self._total_contribution.read(month_id).dev + } + else if(guild == 'design') { + self._total_contribution.read(month_id).design + } + else if(guild == 'problem_solving') { + self._total_contribution.read(month_id).problem_solving + } + else if(guild == 'marcom') { + self._total_contribution.read(month_id).marcom + } + else if(guild == 'research') { + self._total_contribution.read(month_id).research + } + else { + 0 + } } - fn get_marcom_points(self: @ContractState, contributor: ContractAddress) -> u32 { - let contribution = self._contributions.read(contributor); - contribution.marcom + fn get_contributions_data(self: @ContractState, contributor: ContractAddress, guild: felt252) -> Array { + self._contributions_data.read((contributor, guild)) } - fn get_research_points(self: @ContractState, contributor: ContractAddress) -> u32 { - let contribution = self._contributions.read(contributor); - contribution.research + fn get_guild_points(self: @ContractState, contributor: ContractAddress, guild: felt252) -> u32 { + if(guild == 'dev') { + self._contributions.read(contributor).dev + } + else if(guild == 'design') { + self._contributions.read(contributor).design + } + else if(guild == 'problem_solving') { + self._contributions.read(contributor).problem_solving + } + else if(guild == 'marcom') { + self._contributions.read(contributor).marcom + } + else if(guild == 'research') { + self._contributions.read(contributor).research + } + else { + 0 + } } fn get_last_update_id(self: @ContractState) -> u32 { @@ -390,7 +405,7 @@ mod Master { if(new_contribution_score != 0) { let mut contribution_data = self._contributions_data.read((contributor, guild)); contribution_data.append(month_id); - contribution_data.append(new_guild_score); + contribution_data.append(new_contribution_score); self._contributions_data.write((contributor, guild), contribution_data); } diff --git a/src/lib.cairo b/src/lib.cairo index 297dcc2..a8b5977 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -2,4 +2,5 @@ mod master; mod access; mod storage; mod guildSBT; -mod array; \ No newline at end of file +mod array; +mod salary; \ No newline at end of file diff --git a/tests/test_migrate_points.cairo b/tests/test_migrate_points.cairo index c3950a8..1f7a052 100644 --- a/tests/test_migrate_points.cairo +++ b/tests/test_migrate_points.cairo @@ -13,7 +13,6 @@ use contributor_SBT2_0::master::Contribution; #[starknet::interface] trait IMaster { fn get_last_update_id(self: @TContractState) -> u32; - fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> u32; fn get_contributions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); diff --git a/tests/test_mint.cairo b/tests/test_mint.cairo index a32f990..ed50f7e 100644 --- a/tests/test_mint.cairo +++ b/tests/test_mint.cairo @@ -12,7 +12,6 @@ use contributor_SBT2_0::master::MonthlyContribution; #[starknet::interface] trait IMaster { fn get_last_update_id(self: @TContractState) -> u32; - fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> u32; fn update_contibutions(ref self: TContractState, month_id: u32, contributions: Array::); diff --git a/tests/test_update_contribution_points.cairo b/tests/test_update_contribution_points.cairo index 3a6bfe9..df6ac69 100644 --- a/tests/test_update_contribution_points.cairo +++ b/tests/test_update_contribution_points.cairo @@ -14,7 +14,6 @@ use contributor_SBT2_0::master::TotalMonthlyContribution; #[starknet::interface] trait IMaster { fn get_last_update_id(self: @TContractState) -> u32; - fn get_dev_points(self: @TContractState, contributor: ContractAddress) -> u32; fn get_contributions_points(self: @TContractState, contributor: ContractAddress) -> Contribution; fn get_contributions_data(self: @TContractState, contributor: ContractAddress, guild: felt252) -> Array; fn get_total_contribution(self: @TContractState, month_id: u32) -> TotalMonthlyContribution; From c8e534bc85d10c35f0305c9d2616296068959c6c Mon Sep 17 00:00:00 2001 From: yashm001 Date: Wed, 6 Dec 2023 17:40:19 +0530 Subject: [PATCH 20/22] added more getter function --- src/Master.cairo | 17 +++++++++++++++++ src/Master_approach1.cairo | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Master.cairo b/src/Master.cairo index 8a77b6b..e5afd68 100644 --- a/src/Master.cairo +++ b/src/Master.cairo @@ -85,6 +85,7 @@ trait IMaster { fn get_total_contribution(self: @TContractState, month_id: u32) -> TotalMonthlyContribution; fn get_contributions_data(self: @TContractState, contributor: ContractAddress, guild: felt252) -> Array; fn get_guild_total_contribution(self: @TContractState, month_id: u32, guild: felt252) -> u32; + fn get_guild_contribution_for_month(self: @TContractState, contributor: ContractAddress, month_id: u32, guild: felt252) -> u32; // external functions @@ -248,6 +249,22 @@ mod Master { } } + fn get_guild_contribution_for_month(self: @ContractState, contributor: ContractAddress, month_id: u32, guild: felt252) -> u32 { + let contribution_data = self._contributions_data.read((contributor, guild)); + let mut current_index = contribution_data.len(); + let point = loop { + if (current_index == 0) { + break 0; + } + if(month_id == *contribution_data[current_index - 2]) { + break *contribution_data[current_index - 1]; + } + + current_index -= 2; + }; + point + } + fn get_last_update_id(self: @ContractState) -> u32 { self._last_update_id.read() } diff --git a/src/Master_approach1.cairo b/src/Master_approach1.cairo index 9273a88..c4fe1de 100644 --- a/src/Master_approach1.cairo +++ b/src/Master_approach1.cairo @@ -4,7 +4,7 @@ // @notice Master to store contribution points // ************************************ -// @notice this approach is abandoned because issue with storing of array +// @notice this approach is abandoned because issue with storing of array inside struct // Can refer it in future (if possible to implement) // ************************************ From dddf05073aba4537f7599369901c94651eacb111 Mon Sep 17 00:00:00 2001 From: yashm001 Date: Wed, 6 Dec 2023 17:40:36 +0530 Subject: [PATCH 21/22] added attribution contract --- src/array.cairo | 63 +++++++ src/attribution.cairo | 421 ++++++++++++++++++++++++++++++++++++++++++ src/lib.cairo | 2 +- 3 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 src/attribution.cairo diff --git a/src/array.cairo b/src/array.cairo index 9ba6044..3753f17 100644 --- a/src/array.cairo +++ b/src/array.cairo @@ -69,4 +69,67 @@ impl StoreU32Array of Store> { fn size() -> u8 { 255 * Store::::size() } +} + + +impl StoreFelt252Array of Store> { + fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult> { + StoreFelt252Array::read_at_offset(address_domain, base, 0) + } + + fn write( + address_domain: u32, base: StorageBaseAddress, value: Array + ) -> SyscallResult<()> { + StoreFelt252Array::write_at_offset(address_domain, base, 0, value) + } + + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, mut offset: u8 + ) -> SyscallResult> { + let mut arr: Array = ArrayTrait::new(); + + // Read the stored array's length. If the length is superior to 255, the read will fail. + let len: u8 = Store::::read_at_offset(address_domain, base, offset) + .expect('Storage Span too large'); + offset += 1; + + // Sequentially read all stored elements and append them to the array. + let exit = len + offset; + loop { + if offset >= exit { + break; + } + + let value = Store::::read_at_offset(address_domain, base, offset).unwrap(); + arr.append(value); + offset += Store::::size(); + }; + + // Return the array. + Result::Ok(arr) + } + + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, mut offset: u8, mut value: Array + ) -> SyscallResult<()> { + // // Store the length of the array in the first storage slot. + let len: u8 = value.len().try_into().expect('Storage - Span too large'); + Store::::write_at_offset(address_domain, base, offset, len); + offset += 1; + + // Store the array elements sequentially + loop { + match value.pop_front() { + Option::Some(element) => { + Store::::write_at_offset(address_domain, base, offset, element); + offset += Store::::size(); + }, + Option::None(_) => { break Result::Ok(()); } + }; + } + } + + fn size() -> u8 { + 255 * Store::::size() + } } \ No newline at end of file diff --git a/src/attribution.cairo b/src/attribution.cairo new file mode 100644 index 0000000..0f4da7a --- /dev/null +++ b/src/attribution.cairo @@ -0,0 +1,421 @@ +// @title Mesh Contributor SBTs Attribution Cairo 2.2 +// @author Mesh Finance +// @license MIT +// @notice Attribution to store contribution points + +use starknet::ContractAddress; +use array::Array; + + +#[derive(Drop, Serde, starknet::Store)] +struct Contribution { + // @notice cummulative Contribution points for each guild + cum_point: u32, + // @notice timestamp for the last update + last_timestamp: u64 +} + +#[derive(Copy, Drop, Serde, starknet::Store)] +struct MonthlyContribution { + // @notice Contributor Address, used in update_contribution function + contributor: ContractAddress, + // @notice Contribution for guilds + point: u32, +} + + +// +// External Interfaces +// + +#[starknet::interface] +trait IGuild { + fn migrate_sbt(ref self: T, old_address: ContractAddress, new_address: ContractAddress); +} + + +// +// Contract Interface +// +#[starknet::interface] +trait IAttribution { + // view functions + fn get_contributions_points(self: @TContractState, contributor: ContractAddress) -> Array; + fn get_guild_points(self: @TContractState, contributor: ContractAddress, guild: felt252) -> u32; + fn get_last_update_id(self: @TContractState) -> u32; + fn get_last_update_time(self: @TContractState) -> u64; + fn get_migartion_queued_state(self: @TContractState, hash: felt252 ) -> bool; + fn get_guild_SBT(self: @TContractState, guild: felt252) -> ContractAddress; + fn get_contributions_data(self: @TContractState, contributor: ContractAddress, guild: felt252) -> Array; + fn get_guild_total_contribution(self: @TContractState, month_id: u32, guild: felt252) -> u32; + fn get_guild_contribution_for_month(self: @TContractState, contributor: ContractAddress, month_id: u32, guild: felt252) -> u32; + + + // external functions + fn update_contibutions(ref self: TContractState, month_id: u32, guild: felt252, contributions: Array::); + fn initialise(ref self: TContractState, guilds_name: Array::, guilds_address: Array::); + fn add_guild(ref self: TContractState, guild_name: felt252, guild_address: ContractAddress); + fn migrate_points_initiated_by_DAO(ref self: TContractState, old_addresses: Array::, new_addresses: Array::); + fn migrate_points_initiated_by_holder(ref self: TContractState, new_address: ContractAddress); + fn execute_migrate_points_initiated_by_holder(ref self: TContractState, old_address: ContractAddress, new_address: ContractAddress); + +} + + +#[starknet::contract] +mod Attribution { + use traits::Into; // TODO remove intos when u256 inferred type is available + use option::OptionTrait; + use array::{ArrayTrait, SpanTrait}; + use result::ResultTrait; + use zeroable::Zeroable; + use hash::LegacyHash; + use contributor_SBT2_0::access::ownable::{Ownable, IOwnable}; + use contributor_SBT2_0::access::ownable::Ownable::{ + ModifierTrait as OwnableModifierTrait, InternalTrait as OwnableInternalTrait, + }; + use starknet::{ContractAddress, ClassHash, SyscallResult, SyscallResultTrait, get_caller_address, get_contract_address, get_block_timestamp, contract_address_const}; + use integer::{u128_try_from_felt252, u256_sqrt, u256_from_felt252}; + use starknet::syscalls::{replace_class_syscall, call_contract_syscall}; + use contributor_SBT2_0::array::StoreFelt252Array; + use contributor_SBT2_0::array::StoreU32Array; + + use super::{Contribution, MonthlyContribution}; + use super::{ + IGuildDispatcher, IGuildDispatcherTrait + }; + + + // + // Storage Attribution + // + #[storage] + struct Storage { + _contributions: LegacyMap::<(ContractAddress, felt252), Contribution>, // @dev contributions points for each contributor for each guild + _contributions_data: LegacyMap::<(ContractAddress, felt252), Array>, // @dev contributions data for specific contributor and guild + _total_contribution: LegacyMap::<(u32, felt252), u32>, // @dev total contribution month wise [(month_id, guild) => points] + _last_update_id: u32, // @dev contribution update id + _last_update_time: u64, // @dev timestamp for last update + _guilds: Array, // @dev array to store all the guilds + _guild_SBT: LegacyMap::, // @dev contract address for guild SBTs + _initialised: bool, // @dev Flag to store initialisation state + _queued_migrations: LegacyMap::, // @dev flag to store queued migration requests. + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + ContributionUpdated: ContributionUpdated, + MigrationQueued: MigrationQueued, + Migrated: Migrated, + } + + // @notice An event emitted whenever contribution is updated + #[derive(Drop, starknet::Event)] + struct ContributionUpdated { + update_id: u32, + contributor: ContractAddress, + month_id: u32, + guild: felt252, + points_earned: u32 + } + + // @notice An event emitted whenever migration is queued + #[derive(Drop, starknet::Event)] + struct MigrationQueued { + old_address: ContractAddress, + new_address: ContractAddress + } + + // @notice An event emitted whenever SBT is migrated + #[derive(Drop, starknet::Event)] + struct Migrated { + old_address: ContractAddress, + new_address: ContractAddress + } + + + // + // Constructor + // + + // @notice Contract constructor + #[constructor] + fn constructor(ref self: ContractState, owner_: ContractAddress,) { + // @notice not sure if default is already zero or need to initialise. + self._last_update_id.write(0_u32); + self._last_update_time.write(0_u64); + self._initialised.write(false); + + let mut ownable_self = Ownable::unsafe_new_contract_state(); + ownable_self._transfer_ownership(new_owner: owner_); + + } + + #[external(v0)] + impl Attribution of super::IAttribution { + // + // Getters + // + fn get_contributions_points(self: @ContractState, contributor: ContractAddress) -> Array { + let guilds = self._guilds.read(); + let mut contributions = ArrayTrait::::new(); + let mut current_index = 0; + loop { + if (current_index == guilds.len()) { + break; + } + let contribution = self._contributions.read((contributor, *guilds[current_index])); + contributions.append(contribution); + current_index += 1; + }; + contributions + } + + fn get_guild_total_contribution(self: @ContractState, month_id: u32, guild: felt252) -> u32 { + self._total_contribution.read((month_id, guild)) + } + + fn get_contributions_data(self: @ContractState, contributor: ContractAddress, guild: felt252) -> Array { + self._contributions_data.read((contributor, guild)) + } + + fn get_guild_points(self: @ContractState, contributor: ContractAddress, guild: felt252) -> u32 { + self._contributions.read((contributor, guild)).cum_point + } + + fn get_guild_contribution_for_month(self: @ContractState, contributor: ContractAddress, month_id: u32, guild: felt252) -> u32 { + let contribution_data = self._contributions_data.read((contributor, guild)); + let mut current_index = contribution_data.len(); + let point = loop { + if (current_index == 0) { + break 0; + } + if(month_id == *contribution_data[current_index - 2]) { + break *contribution_data[current_index - 1]; + } + + current_index -= 2; + }; + point + } + + fn get_last_update_id(self: @ContractState) -> u32 { + self._last_update_id.read() + } + + fn get_last_update_time(self: @ContractState) -> u64 { + self._last_update_time.read() + } + + fn get_migartion_queued_state(self: @ContractState, hash: felt252 ) -> bool { + self._queued_migrations.read(hash) + } + + fn get_guild_SBT(self: @ContractState, guild: felt252) -> ContractAddress { + self._guild_SBT.read(guild) + } + + + // + // Setters + // + + fn initialise(ref self: ContractState, guilds_name: Array::, guilds_address: Array::) { + self._only_owner(); + let is_initialised = self._initialised.read(); + assert (is_initialised == false, 'ALREADY_INITIALISED'); + self._guilds.write(guilds_name.clone()); + + let mut current_index = 0; + loop { + if (current_index == guilds_name.len()) { + break; + } + self._guild_SBT.write(*guilds_name[current_index], *guilds_address[current_index]); + current_index += 1; + }; + self._initialised.write(true); + } + + fn update_contibutions(ref self: ContractState, month_id: u32, guild: felt252, contributions: Array::) { + self._only_owner(); + let block_timestamp = get_block_timestamp(); + let mut id = self._last_update_id.read(); + let mut current_index = 0; + + // for keeping track of cummulative guild points for that month. + let mut total_cum = 0_u32; + + loop { + if (current_index == contributions.len()) { + break; + } + let new_contributions: MonthlyContribution = *contributions[current_index]; + let contributor: ContractAddress = new_contributions.contributor; + let old_contribution = self._contributions.read((contributor, guild)); + + let new_cum_point = InternalImpl::_update_guild_data(ref self, old_contribution.cum_point, new_contributions.point, month_id, contributor, guild); + + total_cum += new_contributions.point; + + let updated_contribution = Contribution{cum_point: new_cum_point, last_timestamp: block_timestamp}; + self._contributions.write((contributor, guild), updated_contribution); + + current_index += 1; + + self.emit(ContributionUpdated{update_id: id, contributor: contributor, month_id: month_id, guild: guild, points_earned: new_contributions.point}); + + }; + self._total_contribution.write((month_id, guild), total_cum); + + id += 1; + self._last_update_id.write(id); + self._last_update_time.write(block_timestamp); + + } + + fn add_guild(ref self: ContractState, guild_name: felt252, guild_address: ContractAddress) { + self._only_owner(); + let mut guilds = self._guilds.read(); + guilds.append(guild_name); + self._guilds.write(guilds); + self._guild_SBT.write(guild_name, guild_address); + } + + + fn migrate_points_initiated_by_DAO(ref self: ContractState, old_addresses: Array::, new_addresses: Array:: ) { + self._only_owner(); + assert(old_addresses.len() == new_addresses.len(), 'INVALID_INPUTS'); + let mut current_index = 0; + + loop { + if (current_index == old_addresses.len()) { + break; + } + InternalImpl::_migrate_points(ref self, *old_addresses[current_index], *new_addresses[current_index]); + current_index += 1; + }; + + } + + + fn migrate_points_initiated_by_holder(ref self: ContractState, new_address: ContractAddress) { + // TODO: if new address already have any contribution points, if yes return. + let caller = get_caller_address(); + let migration_hash: felt252 = LegacyHash::hash(caller.into(), new_address); + + self._queued_migrations.write(migration_hash, true); + + self.emit(MigrationQueued { old_address: caller, new_address: new_address}); + + } + + // @Notice the function has only_owner modifier to prevent user to use this function to tranfer SBT anytime. + fn execute_migrate_points_initiated_by_holder(ref self: ContractState, old_address: ContractAddress, new_address: ContractAddress) { + self._only_owner(); + let migration_hash: felt252 = LegacyHash::hash(old_address.into(), new_address); + let is_queued = self._queued_migrations.read(migration_hash); + + assert(is_queued == true, 'NOT_QUEUED'); + + InternalImpl::_migrate_points(ref self, old_address, new_address); + self._queued_migrations.write(migration_hash, false); + + } + + + + + } + + #[generate_trait] + impl InternalImpl of InternalTrait { + // + // Internals + // + + fn _update_guild_data(ref self: ContractState, old_guild_score: u32, new_contribution_score: u32, month_id: u32, contributor: ContractAddress, guild: felt252) -> u32 { + let new_guild_score = old_guild_score + new_contribution_score; + if(new_contribution_score != 0) { + let mut contribution_data = self._contributions_data.read((contributor, guild)); + contribution_data.append(month_id); + contribution_data.append(new_contribution_score); + + self._contributions_data.write((contributor, guild), contribution_data); + } + (new_guild_score) + } + + fn _migrate_points(ref self: ContractState, old_address: ContractAddress, new_address: ContractAddress) { + + let guilds = self._guilds.read(); + let mut contributions = ArrayTrait::::new(); + + let mut current_index = 0; + loop { + if (current_index == guilds.len()) { + break; + } + let guild_address = self._guild_SBT.read(*guilds[current_index]); + let contribution = self._contributions.read((old_address, *guilds[current_index])); + // updating contribution data and transfering SBTs + InternalImpl::_update_contribution_data_and_migrate(ref self, old_address, new_address, *guilds[current_index], guild_address); + let zero_contribution = Contribution{cum_point: 0_u32, + last_timestamp: 0_u64 + }; + self._contributions.write((old_address, *guilds[current_index]), zero_contribution); + self._contributions.write((new_address, *guilds[current_index]), contribution); + }; + + self.emit(Migrated{old_address: old_address, new_address: new_address}); + + } + + fn _update_contribution_data_and_migrate(ref self: ContractState, old_address: ContractAddress, new_address: ContractAddress, guild: felt252, guild_contract: ContractAddress) { + let guild_data = self._contributions_data.read((old_address, guild)); + + self._contributions_data.write((new_address, guild), guild_data); + self._contributions_data.write((old_address, guild), ArrayTrait::new()); + + let guildDispatcher = IGuildDispatcher { contract_address: guild_contract }; + guildDispatcher.migrate_sbt(old_address, new_address); + } + + } + + + #[generate_trait] + impl ModifierImpl of ModifierTrait { + fn _only_owner(self: @ContractState) { + let mut ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.assert_only_owner(); + } + } + + #[external(v0)] + impl IOwnableImpl of IOwnable { + fn owner(self: @ContractState) -> ContractAddress { + let ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.owner() + } + + fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { + let mut ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.transfer_ownership(:new_owner); + } + + fn renounce_ownership(ref self: ContractState) { + let mut ownable_self = Ownable::unsafe_new_contract_state(); + + ownable_self.renounce_ownership(); + } + } + + + +} + diff --git a/src/lib.cairo b/src/lib.cairo index a8b5977..4941518 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -1,6 +1,6 @@ mod master; +mod attribution; mod access; mod storage; mod guildSBT; mod array; -mod salary; \ No newline at end of file From e84717c5fef0248de84a74c5f5747a0b91651dc2 Mon Sep 17 00:00:00 2001 From: yashm001 Date: Wed, 6 Dec 2023 17:41:04 +0530 Subject: [PATCH 22/22] added test for attribution contract --- tests/lib.cairo | 1 + tests/test_attribution.cairo | 194 +++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 tests/test_attribution.cairo diff --git a/tests/lib.cairo b/tests/lib.cairo index fe83a70..fbde2ec 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -1,5 +1,6 @@ mod test_deployment; mod test_migrate_points; mod test_mint; +mod test_attribution; mod test_update_contribution_points; mod utils; \ No newline at end of file diff --git a/tests/test_attribution.cairo b/tests/test_attribution.cairo new file mode 100644 index 0000000..fe5f569 --- /dev/null +++ b/tests/test_attribution.cairo @@ -0,0 +1,194 @@ +use array::{Array, ArrayTrait, SpanTrait}; +use result::ResultTrait; +use starknet::ContractAddress; +use starknet::ClassHash; +use traits::TryInto; +use option::OptionTrait; +use snforge_std::{ declare, ContractClassTrait, ContractClass, start_warp, start_prank, stop_prank, + spy_events, SpyOn, EventSpy, EventFetcher, Event, EventAssertions }; +use tests::utils::{ deployer_addr, user1, user2, user3, user4, URI}; +use contributor_SBT2_0::attribution::Contribution; +use contributor_SBT2_0::attribution::MonthlyContribution; + +#[starknet::interface] +trait IAttribution { + fn get_last_update_id(self: @TContractState) -> u32; + fn get_contributions_points(self: @TContractState, contributor: ContractAddress) -> Array; + fn get_contributions_data(self: @TContractState, contributor: ContractAddress, guild: felt252) -> Array; + fn get_guild_total_contribution(self: @TContractState, month_id: u32, guild: felt252) -> u32; + + fn update_contibutions(ref self: TContractState, month_id: u32, guild: felt252, contributions: Array::); + fn initialise(ref self: TContractState, guilds_name: Array::, guilds_address: Array::); + + +/////// + +} + +#[starknet::interface] +trait IGuildSBT { + fn get_contribution_tier(self: @TContractState, contributor: ContractAddress) -> u32; + fn get_master(self: @TContractState) -> ContractAddress; + fn balance_of(self: @TContractState, account: ContractAddress) -> u256; + fn wallet_of_owner(self: @TContractState, account: ContractAddress) -> u256; + fn tokenURI(self: @TContractState, token_id: u256) -> Span; + fn get_next_token_id(self: @TContractState) -> u256; + + fn safe_mint(ref self: TContractState, token_type: u8); +} + + +fn deploy_contracts() -> (ContractAddress, ContractAddress) { + let mut attribution_constructor_calldata = Default::default(); + Serde::serialize(@deployer_addr(), ref attribution_constructor_calldata); + let attribution_class = declare('Attribution'); + let attribution_address = attribution_class.deploy(@attribution_constructor_calldata).unwrap(); + + let name = 'Jedi Dev Guild SBT'; + let symbol = 'JEDI-DEV'; + let mut contribution_levels: Array = ArrayTrait::new(); + contribution_levels.append(100); + contribution_levels.append(200); + contribution_levels.append(500); + contribution_levels.append(1000); + + let mut guildSBT_constructor_calldata = Default::default(); + Serde::serialize(@name, ref guildSBT_constructor_calldata); + Serde::serialize(@symbol, ref guildSBT_constructor_calldata); + Serde::serialize(@URI(), ref guildSBT_constructor_calldata); + Serde::serialize(@deployer_addr(), ref guildSBT_constructor_calldata); + Serde::serialize(@attribution_address, ref guildSBT_constructor_calldata); + Serde::serialize(@contribution_levels, ref guildSBT_constructor_calldata); + + let guildSBT_class = declare('GuildSBT'); + let guildSBT_address = guildSBT_class.deploy(@guildSBT_constructor_calldata).unwrap(); + + let attribution_dispatcher = IAttributionDispatcher { contract_address: attribution_address }; + + let mut guilds_name: Array = ArrayTrait::new(); + guilds_name.append('dev'); + guilds_name.append('design'); + + let mut guilds_address: Array = ArrayTrait::new(); + guilds_address.append(guildSBT_address); + guilds_address.append(guildSBT_address); + + start_prank(attribution_address, deployer_addr()); + attribution_dispatcher.initialise(guilds_name, guilds_address); + stop_prank(attribution_address); + (attribution_address, guildSBT_address) +} + +#[test] +fn test_update_contribution_points() { + let (attribution_address, guildSBT_address) = deploy_contracts(); + let attribution_dispatcher = IAttributionDispatcher { contract_address: attribution_address }; + let guildSBT_dispatcher = IGuildSBTDispatcher { contract_address: guildSBT_address }; + + let id1 = attribution_dispatcher.get_last_update_id(); + assert (id1 == 0, 'Invalid id initialisation'); + + let month_id: u32 = 092023; + let dev_guild = 'dev'; + let design_guild = 'design'; + + let user1_contribution_dev = MonthlyContribution{ contributor: user1(), point: 120}; + let user2_contribution_dev = MonthlyContribution{ contributor: user2(), point: 200}; + let user3_contribution_dev = MonthlyContribution{ contributor: user3(), point: 30}; + let mut contributions_dev: Array = ArrayTrait::new(); + contributions_dev.append(user1_contribution_dev); + contributions_dev.append(user2_contribution_dev); + contributions_dev.append(user3_contribution_dev); + + let user1_contribution_design = MonthlyContribution{ contributor: user1(), point: 10}; + let user2_contribution_design = MonthlyContribution{ contributor: user2(), point: 105}; + let user3_contribution_design = MonthlyContribution{ contributor: user3(), point: 250}; + let mut contributions_design: Array = ArrayTrait::new(); + contributions_design.append(user1_contribution_design); + contributions_design.append(user2_contribution_design); + contributions_design.append(user3_contribution_design); + + + // let mut spy = spy_events(SpyOn::One(attribution_address)); + + start_prank(attribution_address, deployer_addr()); + attribution_dispatcher.update_contibutions(month_id, dev_guild, contributions_dev); + attribution_dispatcher.update_contibutions(month_id, design_guild, contributions_design); + stop_prank(attribution_address); + + // let mut user1_event_data = Default::default(); + // Serde::serialize(@id1, ref user1_event_data); + // Serde::serialize(@user1(), ref user1_event_data); + // Serde::serialize(@month_id, ref user1_event_data); + // Serde::serialize(@dev_guild, ref user1_event_data); + // Serde::serialize(@user1_contribution, ref user1_event_data); + // spy.assert_emitted(@array![ + // Event { from: attribution_address, name: 'ContributionUpdated', keys: array![], data: user1_event_data } + // ]); + + // let mut user2_event_data = Default::default(); + // Serde::serialize(@id1, ref user2_event_data); + // Serde::serialize(@user2(), ref user2_event_data); + // Serde::serialize(@month_id, ref user2_event_data); + // Serde::serialize(@dev_guild, ref user1_event_data); + // Serde::serialize(@user2_contribution, ref user2_event_data); + // spy.assert_emitted(@array![ + // Event { from: attribution_address, name: 'ContributionUpdated', keys: array![], data: user2_event_data } + // ]); + + // let mut user3_event_data = Default::default(); + // Serde::serialize(@id1, ref user3_event_data); + // Serde::serialize(@user3(), ref user3_event_data); + // Serde::serialize(@month_id, ref user3_event_data); + // Serde::serialize(@dev_guild, ref user1_event_data); + // Serde::serialize(@user3_contribution, ref user3_event_data); + // spy.assert_emitted(@array![ + // Event { from: attribution_address, name: 'ContributionUpdated', keys: array![], data: user3_event_data } + // ]); + + let id2 = attribution_dispatcher.get_last_update_id(); + assert (id2 == 2, 'invalid id'); + + // verifying points for user 1 is updated + let user1_points = attribution_dispatcher.get_contributions_points(user1()); + assert(user1_points.len() == 2, 'invalid point length'); + assert(*user1_points.at(0).cum_point == 120, 'invalid dev point'); + assert(*user1_points.at(1).cum_point == 10, 'invalid design point'); + + + // verifying points for user 2 is updated + let user2_points = attribution_dispatcher.get_contributions_points(user2()); + assert(*user2_points.at(0).cum_point == 200, 'invalid dev point'); + assert(*user2_points.at(1).cum_point == 105, 'invalid design point'); + + // verifying contribution data(Montly) is updated + let mut dev_data = attribution_dispatcher.get_contributions_data(user1(), 'dev'); + assert(dev_data.len() == 2, 'invalid length'); + assert(*dev_data[0] == month_id, 'invalid month id'); + assert(*dev_data[1] == 120, 'invalid month points'); + + let mut design_data = attribution_dispatcher.get_contributions_data(user1(), 'design'); + assert(design_data.len() == 2, 'invalid length'); + assert(*design_data[0] == month_id, 'invalid month id'); + assert(*design_data[1] == 10, 'invalid month points'); + + + // verifying tier for each levels + let tier_user1 = guildSBT_dispatcher.get_contribution_tier(user1()); + assert (tier_user1 == 1, 'invalid tier_user1'); + + let tier_user2 = guildSBT_dispatcher.get_contribution_tier(user2()); + assert (tier_user2 == 2, 'invalid tier_user2'); + + + // verifying total monthly contribution + let total_contribution_point_dev = attribution_dispatcher.get_guild_total_contribution(month_id, dev_guild); + assert(total_contribution_point_dev == 350, 'incorrect total dev'); + + // verifying total monthly contribution + let total_contribution_point_design = attribution_dispatcher.get_guild_total_contribution(month_id, design_guild); + assert(total_contribution_point_design == 365, 'incorrect total design'); + + + +} \ No newline at end of file