Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/plmc 473 change edit metadata extrinsic to edit the whole metadata #207

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
045b84a
fix unused imports
JuaniRios Mar 18, 2024
c8153e9
renaming
JuaniRios Mar 18, 2024
282683f
Merge branch 'main' into feature/plmc-138-rename-runtimes
JuaniRios Mar 18, 2024
7b10f2c
Merge branch 'fix/unused-imports' into feature/plmc-138-rename-runtimes
JuaniRios Mar 18, 2024
b13d65c
renamings
JuaniRios Mar 18, 2024
be17dfb
rename node
JuaniRios Mar 18, 2024
90f6998
Merge branch 'main' into feature/plmc-138-rename-runtimes
JuaniRios Mar 18, 2024
3b93956
renamings
JuaniRios Mar 19, 2024
c6ab15e
fmt
JuaniRios Mar 19, 2024
a3b2d6c
new storage item
JuaniRios Mar 19, 2024
336cdb5
impl done, fixes and tests remaining
JuaniRios Mar 19, 2024
ad6238e
Update integration-tests/src/constants.rs
JuaniRios Mar 20, 2024
f60d020
renamings
JuaniRios Mar 21, 2024
d52e720
fmt
JuaniRios Mar 21, 2024
d72e285
merge possible corrupted
JuaniRios Mar 21, 2024
7326f81
fixes
JuaniRios Mar 21, 2024
208e6ee
Merge branch 'feature/plmc-138-rename-runtimes' into feature/plmc-468…
JuaniRios Mar 21, 2024
1a445f6
test fixes for new default multipliers
JuaniRios Mar 21, 2024
3ad65c2
fixes
JuaniRios Mar 21, 2024
7873860
writing tests
JuaniRios Mar 21, 2024
cdda8b7
logging tests
JuaniRios Mar 22, 2024
aad1450
fixes
JuaniRios Mar 22, 2024
e044ada
Merge branch 'main' into feature/plmc-468-limit-multipliers-based-on-…
JuaniRios Mar 22, 2024
ca189dc
fixes
JuaniRios Mar 22, 2024
cb3af92
fix warnings
JuaniRios Mar 22, 2024
5858783
add >0 check on multiplier
JuaniRios Mar 22, 2024
58f7b4d
test for 0 mul check
JuaniRios Mar 22, 2024
97bd10b
update zombienet script
JuaniRios Mar 22, 2024
f69b707
done
JuaniRios Mar 22, 2024
97ef74b
fmt
JuaniRios Mar 22, 2024
ca17d66
Merge branch 'main' into feature/plmc-468-limit-multipliers-based-on-…
JuaniRios Mar 25, 2024
5175f8f
Merge branch 'feature/plmc-468-limit-multipliers-based-on-credential-…
JuaniRios Mar 25, 2024
f4011c2
Merge branch 'main' into feature/plmc-473-change-edit_metadata-extrin…
JuaniRios Mar 25, 2024
df1c5a1
fix
JuaniRios Mar 25, 2024
840da83
Merge branch 'main' into feature/plmc-473-change-edit_metadata-extrin…
JuaniRios Mar 25, 2024
ffcf7be
save
JuaniRios Mar 25, 2024
0935d6d
post-merge fixes and addressing Just's feedback
JuaniRios Mar 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 82 additions & 7 deletions pallets/funding/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,24 +458,98 @@ mod benchmarks {
inst.advance_time(1u32.into()).unwrap();

let issuer = account::<AccountIdOf<T>>("issuer", 0, 0);
let issuer_funding = account::<AccountIdOf<T>>("issuer_funding", 0, 0);
whitelist_account!(issuer);

let project_metadata = default_project::<T>(inst.get_new_nonce(), issuer.clone());
let project_id = inst.create_new_project(project_metadata.clone(), issuer.clone());
let original_metadata_hash = project_metadata.offchain_information_hash.unwrap();
let edited_metadata_hash: H256 = hashed(EDITED_METADATA);

let project_metadata = ProjectMetadataOf::<T> {
token_information: CurrencyMetadata {
name: BoundedVec::try_from("Contribution Token TEST v2".as_bytes().to_vec()).unwrap(),
symbol: BoundedVec::try_from("CTESTv2".as_bytes().to_vec()).unwrap(),
decimals: ASSET_DECIMALS + 2,
},
mainnet_token_max_supply: BalanceOf::<T>::try_from(100_000_000_000_0_000_000_000u128)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
total_allocation_size: BalanceOf::<T>::try_from(200_000_000_0_000_000_000u128)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
auction_round_allocation_percentage: Percent::from_percent(30u8),
minimum_price: 11u128.into(),
bidding_ticket_sizes: BiddingTicketSizes {
professional: TicketSize::new(
Some(
BalanceOf::<T>::try_from(5000 * US_DOLLAR)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
),
Some(
BalanceOf::<T>::try_from(10_000 * US_DOLLAR)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
),
),
institutional: TicketSize::new(
Some(
BalanceOf::<T>::try_from(5000 * US_DOLLAR)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
),
Some(
BalanceOf::<T>::try_from(10_000 * US_DOLLAR)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
),
),
phantom: Default::default(),
},
contributing_ticket_sizes: ContributingTicketSizes {
retail: TicketSize::new(
Some(
BalanceOf::<T>::try_from(5000 * US_DOLLAR)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
),
Some(
BalanceOf::<T>::try_from(10_000 * US_DOLLAR)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
),
),
professional: TicketSize::new(
Some(
BalanceOf::<T>::try_from(5000 * US_DOLLAR)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
),
Some(
BalanceOf::<T>::try_from(10_000 * US_DOLLAR)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
),
),
institutional: TicketSize::new(
Some(
BalanceOf::<T>::try_from(5000 * US_DOLLAR)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
),
Some(
BalanceOf::<T>::try_from(10_000 * US_DOLLAR)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
),
),
phantom: Default::default(),
},
participation_currencies: vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC].try_into().unwrap(),
funding_destination_account: issuer_funding.clone().clone(),
offchain_information_hash: Some(hashed(format!("{}-{}", METADATA, 69)).into()),
};
JuaniRios marked this conversation as resolved.
Show resolved Hide resolved

let jwt = get_mock_jwt(issuer.clone(), InvestorType::Institutional, generate_did_from_account(issuer.clone()));

#[extrinsic_call]
edit_metadata(RawOrigin::Signed(issuer), jwt, project_id, edited_metadata_hash.into());
edit_metadata(RawOrigin::Signed(issuer), jwt, project_id, project_metadata.clone());

// * validity checks *
// Storage
let stored_metadata = ProjectsMetadata::<T>::get(project_id).unwrap();
assert_eq!(stored_metadata.offchain_information_hash, Some(edited_metadata_hash.into()));
assert!(original_metadata_hash != edited_metadata_hash.into());

assert_eq!(stored_metadata, project_metadata);

// Events
frame_system::Pallet::<T>::assert_last_event(Event::<T>::MetadataEdited { project_id }.into());
frame_system::Pallet::<T>::assert_last_event(Event::<T>::MetadataEdited { project_id, metadata: project_metadata }.into());
}

#[benchmark]
Expand Down Expand Up @@ -2218,9 +2292,10 @@ mod benchmarks {
let insertion_block_number: BlockNumberFor<T> = current_block + One::one();

fill_projects_to_update::<T>(x, insertion_block_number);
let jwt = get_mock_jwt(issuer.clone(), InvestorType::Institutional, generate_did_from_account(issuer.clone()));

#[extrinsic_call]
decide_project_outcome(RawOrigin::Signed(issuer), project_id, FundingOutcomeDecision::AcceptFunding);
decide_project_outcome(RawOrigin::Signed(issuer), jwt, project_id, FundingOutcomeDecision::AcceptFunding);

// * validity checks *
// Storage
Expand Down
92 changes: 51 additions & 41 deletions pallets/funding/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -794,46 +794,23 @@ impl<T: Config> Pallet<T> {

// Extrinsics and HRMP interactions
impl<T: Config> Pallet<T> {
/// Called by user extrinsic
/// Creates a project and assigns it to the `issuer` account.
///
/// # Arguments
/// * `issuer` - The account that will be the issuer of the project.
/// * `project` - The project struct containing all the necessary information.
///
/// # Storage access
/// * [`ProjectsMetadata`] - Inserting the main project information. 1 to 1 with the `project` argument.
/// * [`ProjectsDetails`] - Inserting the project information. constructed from the `project` argument.
/// * [`NextProjectId`] - Getting the next usable id, and updating it for the next project.
///
/// # Success path
/// The `project` argument is valid. A ProjectInfo struct is constructed, and the storage is updated
/// with the new structs and mappings to reflect the new project creation
///
/// # Next step
/// The issuer will call an extrinsic to start the evaluation round of the project.
/// [`do_start_evaluation`](Self::do_start_evaluation) will be executed.
#[transactional]
pub fn do_create(issuer: &AccountIdOf<T>, initial_metadata: ProjectMetadataOf<T>, did: Did) -> DispatchResult {
// * Get variables *
let project_id = NextProjectId::<T>::get();
let maybe_active_project = DidWithActiveProjects::<T>::get(did.clone());

// * Validity checks *
ensure!(maybe_active_project == None, Error::<T>::IssuerHasActiveProjectAlready);

if let Err(error) = initial_metadata.is_valid() {
fn project_validation(
metadata: &ProjectMetadataOf<T>,
issuer: AccountIdOf<T>,
did: Did,
) -> Result<(ProjectDetailsOf<T>, BucketOf<T>), DispatchError> {
if let Err(error) = metadata.is_valid() {
return match error {
ValidityError::PriceTooLow => Err(Error::<T>::PriceTooLow.into()),
ValidityError::TicketSizeError => Err(Error::<T>::TicketSizeError.into()),
ValidityError::ParticipationCurrenciesError => Err(Error::<T>::ParticipationCurrenciesError.into()),
};
}
let total_allocation_size = initial_metadata.total_allocation_size;
let total_allocation_size = metadata.total_allocation_size;

// * Calculate new variables *
let fundraising_target =
initial_metadata.minimum_price.checked_mul_int(total_allocation_size).ok_or(Error::<T>::BadMath)?;
metadata.minimum_price.checked_mul_int(total_allocation_size).ok_or(Error::<T>::BadMath)?;
let now = <frame_system::Pallet<T>>::block_number();
let project_details = ProjectDetails {
issuer_account: issuer.clone(),
Expand All @@ -843,7 +820,7 @@ impl<T: Config> Pallet<T> {
fundraising_target,
status: ProjectStatus::Application,
phase_transition_points: PhaseTransitionPoints::new(now),
remaining_contribution_tokens: initial_metadata.total_allocation_size,
remaining_contribution_tokens: metadata.total_allocation_size,
funding_amount_reached: BalanceOf::<T>::zero(),
cleanup: Cleaner::NotReady,
evaluation_round_info: EvaluationRoundInfoOf::<T> {
Expand All @@ -860,7 +837,40 @@ impl<T: Config> Pallet<T> {
},
};

let bucket: BucketOf<T> = Self::create_bucket_from_metadata(&initial_metadata)?;
let bucket: BucketOf<T> = Self::create_bucket_from_metadata(&metadata)?;

Ok((project_details, bucket))
}

/// Called by user extrinsic
/// Creates a project and assigns it to the `issuer` account.
///
/// # Arguments
/// * `issuer` - The account that will be the issuer of the project.
/// * `project` - The project struct containing all the necessary information.
///
/// # Storage access
/// * [`ProjectsMetadata`] - Inserting the main project information. 1 to 1 with the `project` argument.
/// * [`ProjectsDetails`] - Inserting the project information. constructed from the `project` argument.
/// * [`NextProjectId`] - Getting the next usable id, and updating it for the next project.
///
/// # Success path
/// The `project` argument is valid. A ProjectInfo struct is constructed, and the storage is updated
/// with the new structs and mappings to reflect the new project creation
///
/// # Next step
/// The issuer will call an extrinsic to start the evaluation round of the project.
/// [`do_start_evaluation`](Self::do_start_evaluation) will be executed.
#[transactional]
pub fn do_create(issuer: &AccountIdOf<T>, initial_metadata: ProjectMetadataOf<T>, did: Did) -> DispatchResult {
// * Get variables *
let project_id = NextProjectId::<T>::get();
let maybe_active_project = DidWithActiveProjects::<T>::get(did.clone());

// * Validity checks *
ensure!(maybe_active_project == None, Error::<T>::IssuerHasActiveProjectAlready);

let (project_details, bucket) = Self::project_validation(&initial_metadata, issuer.clone(), did.clone())?;

// Each project needs an escrow system account to temporarily hold the USDT/USDC. We need to create it by depositing `ED` amount of PLMC into it.
// This should be paid by the issuer.
Expand Down Expand Up @@ -891,7 +901,6 @@ impl<T: Config> Pallet<T> {
pub fn do_remove_project(issuer: &AccountIdOf<T>, project_id: ProjectId, did: Did) -> DispatchResult {
// * Get variables *
let project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
let project_metadata = ProjectsMetadata::<T>::get(project_id).ok_or(Error::<T>::ProjectNotFound)?;

// * Validity checks *
ensure!(&project_details.issuer_account == issuer, Error::<T>::NotAllowed);
Expand All @@ -917,31 +926,32 @@ impl<T: Config> Pallet<T> {
/// * `project_metadata_hash` - The hash of the image that contains the metadata
///
/// # Storage access
/// * [`Images`] - Check that the image exists
/// * [`ProjectsDetails`] - Check that the project is not frozen
/// * [`ProjectsMetadata`] - Update the metadata hash
#[transactional]
pub fn do_edit_metadata(
issuer: AccountIdOf<T>,
project_id: ProjectId,
project_metadata_hash: T::Hash,
new_project_metadata: ProjectMetadataOf<T>,
) -> DispatchResult {
// * Get variables *
let mut project_metadata = ProjectsMetadata::<T>::get(project_id).ok_or(Error::<T>::ProjectNotFound)?;
let project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;

// * Validity checks *
ensure!(project_details.issuer_account == issuer, Error::<T>::NotAllowed);
ensure!(!project_details.is_frozen, Error::<T>::Frozen);

// * Calculate new variables *
let (project_details, bucket) =
Self::project_validation(&new_project_metadata, issuer.clone(), project_details.issuer_did.clone())?;

// * Update Storage *
project_metadata.offchain_information_hash = Some(project_metadata_hash);
ProjectsMetadata::<T>::insert(project_id, project_metadata);
// * Update storage *
ProjectsMetadata::<T>::insert(project_id, new_project_metadata.clone());
ProjectsDetails::<T>::insert(project_id, project_details);
Buckets::<T>::insert(project_id, bucket);

// * Emit events *
Self::deposit_event(Event::MetadataEdited { project_id });
Self::deposit_event(Event::MetadataEdited { project_id, metadata: new_project_metadata });

Ok(())
}
Expand Down
23 changes: 9 additions & 14 deletions pallets/funding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,10 @@ pub mod pallet {
traits::{OnFinalize, OnIdle, OnInitialize},
};
use frame_system::pallet_prelude::*;
use local_macros::*;
use sp_arithmetic::Percent;
use sp_runtime::{
traits::{Convert, ConvertBack, Get},
DispatchErrorWithPostInfo, TransactionOutcome,
DispatchErrorWithPostInfo,
};

#[cfg(any(feature = "runtime-benchmarks", feature = "std"))]
Expand Down Expand Up @@ -555,11 +554,6 @@ pub mod pallet {
pub type RetailParticipations<T: Config> =
StorageMap<_, Blake2_128Concat, Did, BoundedVec<ProjectId, MaxParticipationsForMaxMultiplier>, ValueQuery>;

#[pallet::storage]
// After 25 participations, the retail user has access to the max multiplier of 10x, so no need to keep tracking it
pub type RetailParticipations<T: Config> =
StorageMap<_, Blake2_128Concat, Did, BoundedVec<ProjectId, MaxParticipationsForMaxMultiplier>, ValueQuery>;

#[pallet::storage]
/// A map to keep track of what issuer's did has an active project. It prevents one issuer having multiple active projects
pub type DidWithActiveProjects<T: Config> = StorageMap<_, Blake2_128Concat, Did, ProjectId, OptionQuery>;
Expand Down Expand Up @@ -587,6 +581,7 @@ pub mod pallet {
/// The metadata of a project was modified.
MetadataEdited {
project_id: ProjectId,
metadata: ProjectMetadataOf<T>,
},
/// The evaluation phase of a project started.
EvaluationStarted {
Expand Down Expand Up @@ -991,6 +986,7 @@ pub mod pallet {
/// The issuer tried to create a new project but already has an active one
IssuerHasActiveProjectAlready,
NotEnoughFunds,
BadMetadata,
}

#[pallet::call]
Expand Down Expand Up @@ -1021,12 +1017,12 @@ pub mod pallet {
origin: OriginFor<T>,
jwt: UntrustedToken,
project_id: ProjectId,
project_metadata_hash: T::Hash,
new_project_metadata: ProjectMetadataOf<T>,
) -> DispatchResult {
let (account, _did, investor_type) =
T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?;
ensure!(investor_type == InvestorType::Institutional, Error::<T>::NotAllowed);
Self::do_edit_metadata(account, project_id, project_metadata_hash)
Self::do_edit_metadata(account, project_id, new_project_metadata)
}

/// Starts the evaluation round of a project. It needs to be called by the project issuer.
Expand Down Expand Up @@ -1275,12 +1271,11 @@ pub mod pallet {
project_id: ProjectId,
outcome: FundingOutcomeDecision,
) -> DispatchResultWithPostInfo {
let caller = ensure_signed(origin)?;
let (account, did, investor_type) =
let (account, _did, investor_type) =
T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?;
ensure!(investor_type == InvestorType::Institutional, Error::<T>::NotAllowed);

Self::do_decide_project_outcome(caller, project_id, outcome)
Self::do_decide_project_outcome(account, project_id, outcome)
}

#[pallet::call_index(18)]
Expand Down Expand Up @@ -1503,7 +1498,7 @@ pub mod pallet {
} else {
*used_weight = used_weight.saturating_add(fallback_weight);
},
Err(DispatchErrorWithPostInfo::<PostDispatchInfo> { error, post_info }) => {
Err(DispatchErrorWithPostInfo::<PostDispatchInfo> { error: _error, post_info }) => {
if let Some(actual_weight) = post_info.actual_weight {
*used_weight = used_weight.saturating_add(actual_weight);
} else {
Expand Down Expand Up @@ -1719,6 +1714,7 @@ pub mod local_macros {

/// used to unwrap storage values that can be Err in places where an error cannot be returned,
/// but an event should be emitted, and skip to the next iteration of a loop
#[allow(unused_macros)]
macro_rules! unwrap_result_or_skip {
($option:expr, $project_id:expr, $error_handler:expr) => {
match $option {
Expand All @@ -1730,5 +1726,4 @@ pub mod local_macros {
}
};
}
pub(crate) use unwrap_result_or_skip;
}
Loading