-
Notifications
You must be signed in to change notification settings - Fork 696
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
Enable Merklized Distributions in Assets Pallet #5400
base: master
Are you sure you want to change the base?
Changes from 70 commits
d50d5e4
7f4422e
bdc0c84
da385ab
6ab4bfb
1cfb29f
3080c7b
39b0fca
599f576
a1c8886
6bde2a3
ead7951
0cac048
acfe734
45f4287
efbe1c9
cfa62ce
885fd94
18a5c2e
5e3e518
f35cacc
e6c3759
89679c3
b40c96a
37052ee
08f2b35
118a51c
44bc5bb
6e853ba
89abcd0
8ad3381
ae6fab7
115f89b
e5f0056
766790a
b80589c
83c66e7
5a4ff43
394cc6f
84574e1
444b1f7
f47b12e
0305bc7
ee90711
61162b6
ae2ff18
fb63207
e713698
988b201
233d339
48fe9c6
122f9cc
4d26deb
8e6fcd5
b17a7c4
fcfda22
6bf7b91
42cdc26
12ed0fb
9c8ab8a
b4f480c
40bd086
7e22961
1b13d9a
1ac0545
7aaabe3
9013156
c98b882
42259eb
2f1f18a
72dd0a1
860f346
1c306f6
3693527
528e71f
b1e5387
9f108c3
fe2e08a
2adc76f
22f26d9
c0a4032
15be9c8
8b5701c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -438,6 +438,128 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
Ok(()) | ||
} | ||
|
||
/// Creates a distribution in storage for asset `id`, which can be claimed via | ||
/// `do_claim_distribution`. | ||
pub(super) fn do_mint_distribution( | ||
id: T::AssetId, | ||
merkle_root: DistributionHashOf<T, I>, | ||
maybe_check_issuer: Option<T::AccountId>, | ||
Ank4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) -> DispatchResult { | ||
let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?; | ||
ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive); | ||
|
||
if let Some(check_issuer) = maybe_check_issuer { | ||
ensure!(check_issuer == details.issuer, Error::<T, I>::NoPermission); | ||
} | ||
|
||
let info = DistributionInfo { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps you have already considered this, but I'm still a little concerned that an account can create a bunch of distributions without paying any storage cost. We could limit this by either
wdyt? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we don't expect multiple active distribution for an asset, probably that is the most straightforward. We can use AssetId as the key for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i am okay with you adding storage deposit to this PR. i would like to hand it off to someone else to own and take to finish line |
||
asset_id: id.clone(), | ||
merkle_root: merkle_root.clone(), | ||
active: true, | ||
}; | ||
|
||
let distribution_id: u32 = MerklizedDistribution::<T, I>::count(); | ||
MerklizedDistribution::<T, I>::insert(&distribution_id, info); | ||
|
||
Self::deposit_event(Event::DistributionIssued { | ||
distribution_id, | ||
asset_id: id, | ||
merkle_root, | ||
}); | ||
|
||
Ok(()) | ||
} | ||
|
||
/// A wrapper around `do_mint`, allowing a `merkle_proof` to control the amount minted and to | ||
/// whom. | ||
pub(super) fn do_claim_distribution( | ||
distribution_id: DistributionCounter, | ||
merkle_proof: Vec<u8>, | ||
) -> DispatchResult { | ||
let proof = | ||
codec::Decode::decode(&mut &merkle_proof[..]).map_err(|_| Error::<T, I>::BadProof)?; | ||
|
||
let DistributionInfo { asset_id, merkle_root, active } = | ||
MerklizedDistribution::<T, I>::get(distribution_id).ok_or(Error::<T, I>::Unknown)?; | ||
|
||
ensure!(active, Error::<T, I>::DistributionEnded); | ||
|
||
let leaf = T::VerifyExistenceProof::verify_proof(proof, &merkle_root) | ||
.map_err(|()| Error::<T, I>::BadProof)?; | ||
let (beneficiary, amount) = | ||
codec::Decode::decode(&mut &leaf[..]).map_err(|_| Error::<T, I>::CannotDecodeLeaf)?; | ||
|
||
ensure!( | ||
!MerklizedDistributionTracker::<T, I>::contains_key(distribution_id, &beneficiary), | ||
Error::<T, I>::AlreadyClaimed | ||
); | ||
|
||
Self::do_mint(asset_id, &beneficiary, amount, None)?; | ||
MerklizedDistributionTracker::<T, I>::insert(&distribution_id, &beneficiary, ()); | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Ends the asset distribution of `distribution_id`. | ||
pub(super) fn do_end_distribution( | ||
distribution_id: DistributionCounter, | ||
maybe_check_issuer: Option<T::AccountId>, | ||
) -> DispatchResult { | ||
let mut info = | ||
MerklizedDistribution::<T, I>::get(&distribution_id).ok_or(Error::<T, I>::Unknown)?; | ||
let details = Asset::<T, I>::get(&info.asset_id).ok_or(Error::<T, I>::Unknown)?; | ||
|
||
if let Some(check_issuer) = maybe_check_issuer { | ||
ensure!(check_issuer == details.issuer, Error::<T, I>::NoPermission); | ||
} | ||
|
||
info.active = false; | ||
|
||
MerklizedDistribution::<T, I>::insert(&distribution_id, info); | ||
Ank4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Self::deposit_event(Event::DistributionEnded { distribution_id }); | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Cleans up the distribution tracker of `distribution_id`. | ||
/// Iterates and cleans up data in the `MerklizedDistributionTracker` map `RemoveItemsLimit` at | ||
/// a time. This function may need to be called multiple times to complete successfully. | ||
pub(super) fn do_destroy_distribution( | ||
distribution_id: DistributionCounter, | ||
) -> DispatchResultWithPostInfo { | ||
let info = | ||
MerklizedDistribution::<T, I>::get(&distribution_id).ok_or(Error::<T, I>::Unknown)?; | ||
|
||
ensure!(!info.active, Error::<T, I>::DistributionActive); | ||
|
||
let mut refund_count = 0u32; | ||
let distribution_iterator = | ||
MerklizedDistributionTracker::<T, I>::iter_key_prefix(&distribution_id); | ||
|
||
let mut all_refunded = true; | ||
for who in distribution_iterator { | ||
if refund_count >= T::RemoveItemsLimit::get() { | ||
// Not everyone was able to be refunded this time around. | ||
all_refunded = false; | ||
break | ||
} | ||
|
||
MerklizedDistributionTracker::<T, I>::remove(&distribution_id, &who); | ||
refund_count += 1; | ||
} | ||
|
||
if all_refunded { | ||
Self::deposit_event(Event::<T, I>::DistributionCleaned { distribution_id }); | ||
// Refund weight only the amount we actually used. | ||
Ok(Some(T::WeightInfo::destroy_distribution(refund_count)).into()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the incentive to clean up state? Shouldn't we refund all fees? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. anyone can clean up state... but yeah we could add a deposit here for doing a distribution |
||
} else { | ||
Self::deposit_event(Event::<T, I>::DistributionPartiallyCleaned { distribution_id }); | ||
// No weight to refund since we did not finish the loop. | ||
Ok(().into()) | ||
} | ||
} | ||
|
||
/// Increases the asset `id` balance of `beneficiary` by `amount`. | ||
/// | ||
/// LOW-LEVEL: Does not alter the supply of asset or emit an event. Use `do_mint` if you need | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO prior to merge