Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 87 additions & 16 deletions pallets/asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ use polymesh_primitives::{
extract_auth,
statistics::TransferManagerResult,
storage_migrate_on, storage_migration_ver, AssetIdentifier, Balance, Document, DocumentId,
IdentityId, PortfolioId, ScopeId, Ticker,
IdentityId, PortfolioId, ScopeId, SecondaryKey, Ticker,
};
use scale_info::TypeInfo;
use sp_runtime::traits::Zero;
Expand Down Expand Up @@ -778,7 +778,44 @@ decl_module! {
/// * `ty` contains the string representation of the asset type.
#[weight = <T as Config>::WeightInfo::register_custom_asset_type(ty.len() as u32)]
pub fn register_custom_asset_type(origin, ty: Vec<u8>) -> DispatchResult {
Self::base_register_custom_asset_type(origin, ty)
Self::base_register_custom_asset_type(origin, ty).map(drop)
}

/// Utility extrinsic to batch `create_asset` and `register_custom_asset_type`.
#[weight = <T as Config>::WeightInfo::create_asset(
name.len() as u32,
identifiers.len() as u32,
funding_round.as_ref().map_or(0, |name| name.len()) as u32
) + <T as Config>::WeightInfo::register_custom_asset_type(custom_asset_type.len() as u32)]
pub fn create_asset_with_custom_type(
origin,
name: AssetName,
ticker: Ticker,
divisible: bool,
custom_asset_type: Vec<u8>,
identifiers: Vec<AssetIdentifier>,
funding_round: Option<FundingRoundName>,
disable_iu: bool,
) -> DispatchResult {
let PermissionedCallOriginData {
primary_did,
secondary_key,
..
} = Identity::<T>::ensure_origin_call_permissions(origin)?;
with_transaction(|| {
let asset_type_id = Self::unsafe_register_custom_asset_type(primary_did, custom_asset_type)?;
Self::unsafe_create_asset(
primary_did,
secondary_key,
name,
ticker,
divisible,
AssetType::Custom(asset_type_id),
identifiers,
funding_round,
disable_iu,
).map(drop)
})
}
}
}
Expand Down Expand Up @@ -1622,6 +1659,35 @@ impl<T: Config> Module<T> {
identifiers: Vec<AssetIdentifier>,
funding_round: Option<FundingRoundName>,
disable_iu: bool,
) -> Result<IdentityId, DispatchError> {
let PermissionedCallOriginData {
primary_did,
secondary_key,
..
} = Identity::<T>::ensure_origin_call_permissions(origin)?;
Self::unsafe_create_asset(
primary_did,
secondary_key,
name,
ticker,
divisible,
asset_type,
identifiers,
funding_round,
disable_iu,
)
}

fn unsafe_create_asset(
did: IdentityId,
secondary_key: Option<SecondaryKey<T::AccountId>>,
name: AssetName,
ticker: Ticker,
divisible: bool,
asset_type: AssetType,
identifiers: Vec<AssetIdentifier>,
funding_round: Option<FundingRoundName>,
disable_iu: bool,
) -> Result<IdentityId, DispatchError> {
Self::ensure_asset_name_bounded(&name)?;
if let Some(fr) = &funding_round {
Expand All @@ -1630,12 +1696,6 @@ impl<T: Config> Module<T> {
Self::ensure_asset_idents_valid(&identifiers)?;
Self::ensure_asset_type_valid(asset_type)?;

let PermissionedCallOriginData {
primary_did: did,
secondary_key,
..
} = Identity::<T>::ensure_origin_call_permissions(origin)?;

Self::ensure_create_asset_parameters(&ticker)?;

// Ensure its registered by DID or at least expired, thus available.
Expand Down Expand Up @@ -2248,21 +2308,32 @@ impl<T: Config> Module<T> {
)
}

fn base_register_custom_asset_type(origin: T::Origin, ty: Vec<u8>) -> DispatchResult {
ensure_string_limited::<T>(&ty)?;

fn base_register_custom_asset_type(
origin: T::Origin,
ty: Vec<u8>,
) -> Result<CustomAssetTypeId, DispatchError> {
let did = Identity::<T>::ensure_perms(origin)?;
Self::unsafe_register_custom_asset_type(did, ty)
}

match CustomTypesInverse::try_get(&ty) {
Ok(id) => Self::deposit_event(Event::<T>::CustomAssetTypeExists(did, id, ty)),
fn unsafe_register_custom_asset_type(
did: IdentityId,
ty: Vec<u8>,
) -> Result<CustomAssetTypeId, DispatchError> {
ensure_string_limited::<T>(&ty)?;

Ok(match CustomTypesInverse::try_get(&ty) {
Ok(id) => {
Self::deposit_event(Event::<T>::CustomAssetTypeExists(did, id, ty));
id
}
Err(()) => {
let id = CustomTypeIdSequence::try_mutate(try_next_pre::<T, _>)?;
CustomTypesInverse::insert(&ty, id);
CustomTypes::insert(id, ty.clone());
Self::deposit_event(Event::<T>::CustomAssetTypeRegistered(did, id, ty));
id
}
}

Ok(())
})
}
}
2 changes: 2 additions & 0 deletions pallets/common/src/traits/external_agents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use polymesh_primitives::{EventDid, ExtrinsicPermissions, IdentityId, Ticker};

pub trait WeightInfo {
fn create_group(p: u32) -> Weight;
fn create_group_and_add_auth(p: u32) -> Weight;
fn create_and_change_custom_group(p: u32) -> Weight;
fn set_group_permissions(p: u32) -> Weight;
fn remove_agent() -> Weight;
fn abdicate() -> Weight;
Expand Down
131 changes: 85 additions & 46 deletions pallets/corporate-actions/src/distribution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ use polymesh_common_utilities::{
};
use polymesh_primitives::{
storage_migration_ver, Balance, EventDid, IdentityId, Moment, PortfolioId, PortfolioNumber,
Ticker,
SecondaryKey, Ticker,
};
use scale_info::TypeInfo;
#[cfg(feature = "std")]
Expand Down Expand Up @@ -217,62 +217,24 @@ decl_module! {
amount: Balance,
payment_at: Moment,
expires_at: Option<Moment>,
) {
// Ensure CA's asset is distinct from the distributed currency.
ensure!(ca_id.ticker != currency, Error::<T>::DistributingAsset);

// Ensure that any expiry date doesn't come before the payment date.
ensure!(!expired(expires_at, payment_at), Error::<T>::ExpiryBeforePayment);

// Ensure CA doesn't have a distribution yet.
ensure!(!Distributions::contains_key(ca_id), Error::<T>::AlreadyExists);

// Ensure origin is a permissioned agent and that they have custody over `from`.
// Also ensure secondary key has perms for `from` + portfolio is valid.
) -> DispatchResult {
let PermissionedCallOriginData {
primary_did: agent,
secondary_key,
..
} = <ExternalAgents<T>>::ensure_agent_asset_perms(origin, ca_id.ticker)?;
let from = PortfolioId { did: agent, kind: portfolio.into() };
<Portfolio<T>>::ensure_portfolio_custody_and_permission(from, agent, secondary_key.as_ref())?;
<Portfolio<T>>::ensure_portfolio_validity(&from)?;

// Ensure that `ca_id` exists, that its a benefit.
let agent = agent.for_event();
let ca = <CA<T>>::ensure_ca_exists(ca_id)?;
ensure!(ca.kind.is_benefit(), Error::<T>::CANotBenefit);

// Ensure CA has a record `date <= payment_at`.
// If we cannot, deriving a checkpoint,
// used to determine each holder's allotment of the total `amount`,
// is not possible.
<CA<T>>::ensure_record_date_before_start(&ca, payment_at)?;

// Ensure `from` has at least `amount` to later lock (1).
<Portfolio<T>>::ensure_sufficient_balance(&from, &currency, amount)?;

// Charge the protocol fee. Last check; we are in commit phase after this.
T::ProtocolFee::charge_fee(ProtocolOp::CapitalDistributionDistribute)?;

// (1) Lock `amount` in `from`.
<Portfolio<T>>::unchecked_lock_tokens(&from, &currency, amount);

// Commit to storage.
let distribution = Distribution {
from,
Self::unsafe_distribute(
agent,
secondary_key,
ca_id,
portfolio,
currency,
per_share,
amount,
remaining: amount,
reclaimed: false,
payment_at,
expires_at,
};
Distributions::insert(ca_id, distribution);

// Emit event.
Self::deposit_event(Event::Created(agent, ca_id, distribution));
)
}

/// Claim a benefit of the capital distribution attached to `ca_id`.
Expand Down Expand Up @@ -572,4 +534,81 @@ impl<T: Config> Module<T> {
);
Ok(dist)
}

pub fn unsafe_distribute(
agent: IdentityId,
secondary_key: Option<SecondaryKey<T::AccountId>>,
ca_id: CAId,
portfolio: Option<PortfolioNumber>,
currency: Ticker,
per_share: Balance,
amount: Balance,
payment_at: Moment,
expires_at: Option<Moment>,
) -> DispatchResult {
// Ensure CA's asset is distinct from the distributed currency.
ensure!(ca_id.ticker != currency, Error::<T>::DistributingAsset);

// Ensure that any expiry date doesn't come before the payment date.
ensure!(
!expired(expires_at, payment_at),
Error::<T>::ExpiryBeforePayment
);

// Ensure CA doesn't have a distribution yet.
ensure!(
!Distributions::contains_key(ca_id),
Error::<T>::AlreadyExists
);

// Ensure secondary key has perms for `from` + portfolio is valid.
let from = PortfolioId {
did: agent,
kind: portfolio.into(),
};
<Portfolio<T>>::ensure_portfolio_custody_and_permission(
from,
agent,
secondary_key.as_ref(),
)?;
<Portfolio<T>>::ensure_portfolio_validity(&from)?;

// Ensure that `ca_id` exists, that its a benefit.
let agent = agent.for_event();
let ca = <CA<T>>::ensure_ca_exists(ca_id)?;
ensure!(ca.kind.is_benefit(), Error::<T>::CANotBenefit);

// Ensure CA has a record `date <= payment_at`.
// If we cannot, deriving a checkpoint,
// used to determine each holder's allotment of the total `amount`,
// is not possible.
<CA<T>>::ensure_record_date_before_start(&ca, payment_at)?;

// Ensure `from` has at least `amount` to later lock (1).
<Portfolio<T>>::ensure_sufficient_balance(&from, &currency, amount)?;

// Charge the protocol fee. Last check; we are in commit phase after this.
T::ProtocolFee::charge_fee(ProtocolOp::CapitalDistributionDistribute)?;

// (1) Lock `amount` in `from`.
<Portfolio<T>>::unchecked_lock_tokens(&from, &currency, amount);

// Commit to storage.
let distribution = Distribution {
from,
currency,
per_share,
amount,
remaining: amount,
reclaimed: false,
payment_at,
expires_at,
};
Distributions::insert(ca_id, distribution);

// Emit event.
Self::deposit_event(Event::Created(agent, ca_id, distribution));

Ok(())
}
}
Loading