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 34 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
161 changes: 156 additions & 5 deletions pallets/funding/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,21 +432,172 @@ 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 optional_project_metadata = OptionalProjectMetadataOf::<T> {
token_information: Some(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: Some(
BalanceOf::<T>::try_from(100_000_000_000_0_000_000_000u128)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
),
total_allocation_size: Some(
BalanceOf::<T>::try_from(200_000_000_0_000_000_000u128)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
),
auction_round_allocation_percentage: Some(Percent::from_percent(30u8)),
minimum_price: Some(11u128.into()),
bidding_ticket_sizes: Some(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: Some(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: Some(
vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC].try_into().unwrap(),
),
funding_destination_account: Some(issuer_funding.clone().clone()),
offchain_information_hash: Some(Some(hashed(format!("{}-{}", METADATA, 69)).into())),
};

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

// * 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());
let expected_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,
offchain_information_hash: Some(hashed(format!("{}-{}", METADATA, 69)).into()),
};
assert_eq!(stored_metadata, expected_metadata);

// Events
frame_system::Pallet::<T>::assert_last_event(Event::<T>::MetadataEdited { project_id }.into());
Expand Down
12 changes: 9 additions & 3 deletions pallets/funding/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,7 @@ impl<T: Config> Pallet<T> {
pub fn do_edit_metadata(
issuer: AccountIdOf<T>,
project_id: ProjectId,
project_metadata_hash: T::Hash,
optional_project_metadata: OptionalProjectMetadataOf<T>,
) -> DispatchResult {
// * Get variables *
let mut project_metadata = ProjectsMetadata::<T>::get(project_id).ok_or(Error::<T>::ProjectNotFound)?;
Expand All @@ -907,12 +907,18 @@ impl<T: Config> Pallet<T> {
// * Validity checks *
ensure!(project_details.issuer_account == issuer, Error::<T>::NotAllowed);
ensure!(!project_details.is_frozen, Error::<T>::Frozen);
ensure!(!Images::<T>::contains_key(project_metadata_hash), Error::<T>::MetadataAlreadyExists);

// * Calculate new variables *
project_metadata.edit_fields_from(optional_project_metadata);
if let Err(error) = project_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()),
};
}

// * Update Storage *
project_metadata.offchain_information_hash = Some(project_metadata_hash);
ProjectsMetadata::<T>::insert(project_id, project_metadata);

// * Emit events *
Expand Down
11 changes: 9 additions & 2 deletions pallets/funding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ pub type EvaluatorsOutcomeOf<T> = EvaluatorsOutcome<BalanceOf<T>>;
pub type TicketSizeOf<T> = TicketSize<BalanceOf<T>>;
pub type ProjectMetadataOf<T> =
ProjectMetadata<BoundedVec<u8, StringLimitOf<T>>, BalanceOf<T>, PriceOf<T>, AccountIdOf<T>, HashOf<T>>;
pub type OptionalProjectMetadataOf<T> =
OptionalProjectMetadata<BoundedVec<u8, StringLimitOf<T>>, BalanceOf<T>, PriceOf<T>, AccountIdOf<T>, HashOf<T>>;
pub type ProjectDetailsOf<T> =
ProjectDetails<AccountIdOf<T>, Did, BlockNumberFor<T>, PriceOf<T>, BalanceOf<T>, EvaluationRoundInfoOf<T>>;
pub type EvaluationRoundInfoOf<T> = EvaluationRoundInfo<BalanceOf<T>>;
Expand Down Expand Up @@ -556,6 +558,10 @@ pub mod pallet {
BalanceOf<T>,
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]
// After 25 participations, the retail user has access to the max multiplier of 10x, so no need to keep tracking it
Expand Down Expand Up @@ -989,6 +995,7 @@ pub mod pallet {
/// The issuer tried to create a new project but already has an active one
IssuerHasActiveProjectAlready,
NotEnoughFunds,
BadMetadata,
}

#[pallet::call]
Expand All @@ -1011,12 +1018,12 @@ pub mod pallet {
origin: OriginFor<T>,
jwt: UntrustedToken,
project_id: ProjectId,
project_metadata_hash: T::Hash,
optional_project_metadata: OptionalProjectMetadataOf<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, optional_project_metadata)
}

/// Starts the evaluation round of a project. It needs to be called by the project issuer.
Expand Down
116 changes: 116 additions & 0 deletions pallets/funding/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,122 @@ mod creation {
)));
}

#[test]
fn edit_metadata() {
let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext())));
let project_metadata = ProjectMetadata {
token_information: default_token_information(),
mainnet_token_max_supply: 8_000_000 * ASSET_UNIT,
total_allocation_size: 1_000_000 * ASSET_UNIT,
auction_round_allocation_percentage: Percent::from_percent(50u8),
minimum_price: PriceOf::<TestRuntime>::from_float(10.0),
bidding_ticket_sizes: BiddingTicketSizes {
professional: TicketSize::new(Some(5000 * US_DOLLAR), None),
institutional: TicketSize::new(Some(5000 * US_DOLLAR), None),
phantom: Default::default(),
},
contributing_ticket_sizes: ContributingTicketSizes {
retail: TicketSize::new(None, None),
professional: TicketSize::new(None, None),
institutional: TicketSize::new(None, None),
phantom: Default::default(),
},
participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(),
funding_destination_account: ISSUER_1,
offchain_information_hash: None,
};
inst.mint_plmc_to(default_plmc_balances());
let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1));
let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1);

let new_metadata_1 = OptionalProjectMetadataOf::<TestRuntime> {
token_information: None,
mainnet_token_max_supply: None,
total_allocation_size: None,
auction_round_allocation_percentage: None,
minimum_price: Some(PriceOf::<TestRuntime>::from_float(15.0)),
bidding_ticket_sizes: None,
contributing_ticket_sizes: None,
participation_currencies: None,
funding_destination_account: None,
offchain_information_hash: None,
};
let new_metadata_2 = OptionalProjectMetadataOf::<TestRuntime> {
token_information: Some(CurrencyMetadata {
name: BoundedVec::try_from("Changed Name".as_bytes().to_vec()).unwrap(),
symbol: BoundedVec::try_from("CN".as_bytes().to_vec()).unwrap(),
decimals: 12,
}),
mainnet_token_max_supply: Some(100_000_000 * ASSET_UNIT),
total_allocation_size: Some(50_000_000 * ASSET_UNIT),
auction_round_allocation_percentage: Some(Percent::from_percent(30u8)),
minimum_price: Some(PriceOf::<TestRuntime>::from_float(20.0)),
bidding_ticket_sizes: Some(BiddingTicketSizes {
professional: TicketSize::new(Some(10_000 * US_DOLLAR), Some(20_000 * US_DOLLAR)),
institutional: TicketSize::new(Some(20_000 * US_DOLLAR), Some(30_000 * US_DOLLAR)),
phantom: Default::default(),
}),
contributing_ticket_sizes: Some(ContributingTicketSizes {
retail: TicketSize::new(Some(1_000 * US_DOLLAR), Some(2_000 * US_DOLLAR)),
professional: TicketSize::new(Some(2_000 * US_DOLLAR), Some(3_000 * US_DOLLAR)),
institutional: TicketSize::new(Some(3_000 * US_DOLLAR), Some(4_000 * US_DOLLAR)),
phantom: Default::default(),
}),
participation_currencies: Some(
vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC].try_into().unwrap(),
),
funding_destination_account: Some(ISSUER_2),
offchain_information_hash: Some(Some(hashed(METADATA))),
};

// Just one field should change
assert_ok!(inst.execute(|| crate::Pallet::<TestRuntime>::edit_metadata(
RuntimeOrigin::signed(ISSUER_1),
jwt.clone(),
project_id,
new_metadata_1.clone()
)));
let mut expected_metadata = project_metadata.clone();
expected_metadata.minimum_price = new_metadata_1.minimum_price.unwrap();
assert_eq!(inst.get_project_metadata(project_id), expected_metadata);

// All fields changed
assert_ok!(inst.execute(|| crate::Pallet::<TestRuntime>::edit_metadata(
RuntimeOrigin::signed(ISSUER_1),
jwt.clone(),
project_id,
new_metadata_2.clone()
)));
let stored_metadata = inst.get_project_metadata(project_id);
assert_eq!(stored_metadata.token_information, new_metadata_2.token_information.unwrap());
assert_eq!(stored_metadata.mainnet_token_max_supply, new_metadata_2.mainnet_token_max_supply.unwrap());
assert_eq!(stored_metadata.total_allocation_size, new_metadata_2.total_allocation_size.unwrap());
assert_eq!(
stored_metadata.auction_round_allocation_percentage,
new_metadata_2.auction_round_allocation_percentage.unwrap()
);
assert_eq!(stored_metadata.minimum_price, new_metadata_2.minimum_price.unwrap());
assert_eq!(stored_metadata.bidding_ticket_sizes, new_metadata_2.bidding_ticket_sizes.unwrap());
assert_eq!(stored_metadata.contributing_ticket_sizes, new_metadata_2.contributing_ticket_sizes.unwrap());
assert_eq!(stored_metadata.participation_currencies, new_metadata_2.participation_currencies.unwrap());
assert_eq!(stored_metadata.funding_destination_account, new_metadata_2.funding_destination_account.unwrap());
assert_eq!(stored_metadata.offchain_information_hash, new_metadata_2.offchain_information_hash.unwrap());

// Cannot edit after evaluation started
inst.start_evaluation(project_id, ISSUER_1).unwrap();
inst.execute(|| {
assert_noop!(
Pallet::<TestRuntime>::edit_metadata(
RuntimeOrigin::signed(ISSUER_1),
jwt.clone(),
project_id,
new_metadata_1
),
Error::<TestRuntime>::Frozen
);
});
}

#[test]
fn basic_plmc_transfer_works() {
let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext())));
Expand Down
Loading