{
+ pub oracle_manager_badge: T,
+ pub protocol_owner_badge: T,
+ pub protocol_manager_badge: T,
+}
+
+pub enum BadgeHandling {
+ /// Creates a new badge and deposits it into the specified account.
+ CreateAndSend {
+ /// The account that the badges should be sent to.
+ account_address: ComponentAddress,
+ /// The metadata of the created badges.
+ metadata_init: MetadataInit,
+ },
+ /// Use an existing badge that exists in some account. If the badge is
+ /// required in one of the operations then a proof of it will be created.
+ /// A signature of this account must be provided.
+ UseExisting {
+ /// The private key of the account that controlling the badge. This is
+ /// required for any proofs that need to be created.
+ controlling_private_key: PrivateKey,
+ /// The address of the holder
+ holder_account_address: ComponentAddress,
+ /// The address of the badge
+ badge_resource_address: ResourceAddress,
+ },
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
+pub struct ExchangeInformation {
+ /// The id of the pool blueprint of the exchange.
+ pub blueprint_id: BlueprintId,
+ /// The pools that we wish to support for the exchange.
+ pub pools: UserResourceIndexedData
,
+ /// The liquidity receipt to use for the exchange.
+ pub liquidity_receipt: R,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
+pub enum PackageHandling {
+ /// The package should be compiled and published in the process.
+ LoadAndPublish {
+ /// The name of the crate that contains the package. This is the name
+ /// that will be used when instructing the package loader to get the
+ /// package.
+ crate_package_name: String,
+ /// The initial metadata to set on the package when it's being published
+ metadata: MetadataInit,
+ /// The name of the blueprint to use from this package. This is under
+ /// the assumption that each package is just a single blueprint.
+ blueprint_name: String,
+ },
+ /// The package already exists on the desired network.
+ UseExisting {
+ /// The address of the package on the network and
+ package_address: BlueprintId,
+ },
+}
+
+#[derive(
+ Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ScryptoSbor,
+)]
+pub enum PoolHandling {
+ /// A pool does not exist and should be created.
+ Create,
+ /// A pool already exists and should be used.
+ UseExisting {
+ /// The address of the pool to use
+ pool_address: ComponentAddress,
+ },
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
+pub enum UserResourceHandling {
+ /// Resources do not exist on the network and should be created
+ CreateFreelyMintableAndBurnable {
+ /// The divisibility to create the resource with
+ divisibility: u8,
+ /// The initial metadata to use for the resource
+ metadata: MetadataInit,
+ },
+ /// Resources exist on the network and should be used.
+ UseExisting {
+ /// The address of the resource
+ resource_address: ResourceAddress,
+ },
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
+pub enum LiquidityReceiptHandling {
+ /// Create a new resource to use as the liquidity receipt
+ CreateNew {
+ /// The non-fungible data schema of the resource.
+ non_fungible_schema: NonFungibleDataSchema,
+ /// The initial metadata to use for the resource.
+ metadata: MetadataInit,
+ },
+ /// Use an existing resource as the liquidity receipt of the exchange
+ UseExisting {
+ /// The address of the liquidity receipt resource
+ resource_address: ResourceAddress,
+ },
+}
diff --git a/tools/publishing-tool/src/publishing/error.rs b/tools/publishing-tool/src/publishing/error.rs
new file mode 100644
index 00000000..3d75c1eb
--- /dev/null
+++ b/tools/publishing-tool/src/publishing/error.rs
@@ -0,0 +1,4 @@
+#[derive(Clone, Debug)]
+pub struct KeyNotFound {
+ pub key: String,
+}
diff --git a/tools/publishing-tool/src/publishing/handler.rs b/tools/publishing-tool/src/publishing/handler.rs
new file mode 100644
index 00000000..98c170c6
--- /dev/null
+++ b/tools/publishing-tool/src/publishing/handler.rs
@@ -0,0 +1,1579 @@
+#![allow(clippy::arithmetic_side_effects, clippy::too_many_arguments)]
+
+use defiplaza_v2_adapter_v1::*;
+use ignition::{InitializationParametersManifest, PoolBlueprintInformation};
+use itertools::*;
+use ociswap_v2_adapter_v1::OciswapV2PoolInterfaceManifestBuilderExtensionTrait;
+use package_loader::*;
+use radix_engine::blueprints::package::*;
+use radix_engine::types::node_modules::*;
+use radix_engine_interface::blueprints::account::*;
+use rand::prelude::*;
+use transaction::prelude::*;
+
+use super::*;
+use crate::network_connection_provider::*;
+
+pub fn publish(
+ configuration: &PublishingConfiguration,
+ network_provider: &mut N,
+) -> Result<
+ PublishingReceipt,
+ PublishingError<::Error>,
+> {
+ // A cryptographically secure random number generator.
+ let mut std_rng = rand::rngs::StdRng::from_entropy();
+
+ // Creating an ephemeral private key to use for the publishing process. This
+ // key will be mapped to an account that may store things during the process
+ // but will ultimately be discarded in the end.
+ let ephemeral_key_u64 = std_rng.next_u64();
+ let ephemeral_private_key = PrivateKey::Ed25519(
+ Ed25519PrivateKey::from_u64(ephemeral_key_u64).unwrap(),
+ );
+ let ephemeral_account = ComponentAddress::virtual_account_from_public_key(
+ &ephemeral_private_key.public_key(),
+ );
+ log::info!("Ephemeral private key selected: {}", ephemeral_key_u64);
+
+ // Finding the set of private keys to use for the signatures. This will be
+ // the notary, the fee payer, and all of the private keys that control the
+ // accounts with the badges.
+ let mut signer_private_keys = vec![
+ &configuration.transaction_configuration.notary,
+ &ephemeral_private_key,
+ &configuration
+ .transaction_configuration
+ .fee_payer_information
+ .controlling_key,
+ ];
+
+ for badge_handling in configuration.badges.iter() {
+ if let BadgeHandling::UseExisting {
+ controlling_private_key,
+ ..
+ } = badge_handling
+ {
+ signer_private_keys.push(controlling_private_key)
+ }
+ }
+
+ // Creating an execution service from the passed executor
+ let mut execution_service = ExecutionService::new(
+ network_provider,
+ configuration
+ .transaction_configuration
+ .fee_payer_information
+ .account_address,
+ &configuration.transaction_configuration.notary,
+ &signer_private_keys,
+ );
+
+ // Creating the dApp definition account. The owner role will be set to the
+ // ephemeral private key and then switched to the protocol owner and manager
+ // at the end
+ let dapp_definition_account = {
+ let manifest = ManifestBuilder::new()
+ .allocate_global_address(
+ ACCOUNT_PACKAGE,
+ ACCOUNT_BLUEPRINT,
+ "reservation",
+ "named_address",
+ )
+ .then(|builder| {
+ let reservation = builder.address_reservation("reservation");
+ let named_address = builder.named_address("named_address");
+
+ let mut builder = builder
+ .call_function(
+ ACCOUNT_PACKAGE,
+ ACCOUNT_BLUEPRINT,
+ ACCOUNT_CREATE_ADVANCED_IDENT,
+ AccountCreateAdvancedManifestInput {
+ address_reservation: Some(reservation),
+ owner_role: OwnerRole::Updatable(rule!(require(
+ NonFungibleGlobalId::from_public_key(
+ &ephemeral_private_key.public_key()
+ )
+ ))),
+ },
+ )
+ .call_metadata_method(
+ named_address,
+ METADATA_SET_IDENT,
+ MetadataSetInput {
+ key: "account_type".to_owned(),
+ value: MetadataValue::String(
+ "dapp definition".to_owned(),
+ ),
+ },
+ )
+ .call_metadata_method(
+ named_address,
+ METADATA_SET_IDENT,
+ MetadataSetInput {
+ key: "claimed_websites".to_owned(),
+ value: MetadataValue::OriginArray(vec![]),
+ },
+ )
+ .call_metadata_method(
+ named_address,
+ METADATA_SET_IDENT,
+ MetadataSetInput {
+ key: "dapp_definitions".to_owned(),
+ value: MetadataValue::GlobalAddressArray(vec![]),
+ },
+ );
+
+ for (key, value) in
+ configuration.dapp_definition_metadata.iter()
+ {
+ builder = builder.call_metadata_method(
+ named_address,
+ METADATA_SET_IDENT,
+ MetadataSetInput {
+ key: key.to_owned(),
+ value: value.clone(),
+ },
+ )
+ }
+
+ builder
+ })
+ .build();
+
+ execution_service
+ .execute_manifest(manifest.clone())?
+ .new_entities
+ .new_component_addresses
+ .first()
+ .copied()
+ .expect("Must succeed!")
+ };
+
+ // Handling the creation of the user resources if they need to be created.
+ let resolved_user_resources = {
+ let user_resources_map = configuration.user_resources.into_map();
+
+ let user_resources_already_created =
+ user_resources_map.iter().flat_map(|(key, handling)| {
+ if let UserResourceHandling::UseExisting { resource_address } =
+ handling
+ {
+ Some((*key, resource_address))
+ } else {
+ None
+ }
+ });
+ let user_resources_requiring_creation = user_resources_map
+ .iter()
+ .flat_map(|(key, handling)| {
+ if let UserResourceHandling::CreateFreelyMintableAndBurnable {
+ divisibility,
+ metadata,
+ } = handling
+ {
+ Some((*key, (divisibility, metadata)))
+ } else {
+ None
+ }
+ })
+ .collect::>();
+
+ // Construct a manifest that creates the user resources.
+ let manifest = TransactionManifestV1 {
+ instructions: user_resources_requiring_creation
+ .values()
+ .map(|(divisibility, metadata)| InstructionV1::CallFunction {
+ package_address: RESOURCE_PACKAGE.into(),
+ blueprint_name: FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT
+ .to_string(),
+ function_name: FUNGIBLE_RESOURCE_MANAGER_CREATE_IDENT
+ .to_owned(),
+ args: to_manifest_value(
+ &FungibleResourceManagerCreateManifestInput {
+ owner_role: OwnerRole::None,
+ track_total_supply: true,
+ divisibility: **divisibility,
+ resource_roles: FungibleResourceRoles {
+ mint_roles: mint_roles! {
+ minter => rule!(allow_all);
+ minter_updater => rule!(deny_all);
+ },
+ burn_roles: burn_roles! {
+ burner => rule!(allow_all);
+ burner_updater => rule!(deny_all);
+ },
+ ..Default::default()
+ },
+ metadata: ModuleConfig {
+ init: (*metadata).clone(),
+ roles: Default::default(),
+ },
+ address_reservation: None,
+ },
+ )
+ .expect("Can't fail!"),
+ })
+ .collect::>(),
+ blobs: Default::default(),
+ };
+ let resource_addresses = execution_service
+ .execute_manifest(manifest)?
+ .new_entities
+ .new_resource_addresses;
+
+ UserResourceIndexedData::from_map(
+ user_resources_already_created
+ .map(|(key, address)| (key, *address))
+ .chain(
+ user_resources_requiring_creation
+ .iter()
+ .map(|value| *value.0)
+ .zip(resource_addresses),
+ ),
+ )
+ .expect("Can't fail!")
+ };
+
+ // Handling the badge creation that is needed.
+ let resolved_badges = {
+ let already_existing_badges =
+ configuration.badges.into_map().into_iter().filter_map(
+ |(key, value)| {
+ if let BadgeHandling::UseExisting {
+ holder_account_address,
+ badge_resource_address,
+ ..
+ } = value
+ {
+ Some((
+ key,
+ (*holder_account_address, *badge_resource_address),
+ ))
+ } else {
+ None
+ }
+ },
+ );
+
+ let badges_requiring_creation = configuration
+ .badges
+ .into_map()
+ .into_iter()
+ .filter_map(|(key, value)| {
+ if let BadgeHandling::CreateAndSend { metadata_init, .. } =
+ value
+ {
+ Some((key, metadata_init))
+ } else {
+ None
+ }
+ });
+
+ let mut manifest_builder = ManifestBuilder::new();
+ let mut keys = vec![];
+ for (key, metadata_init) in badges_requiring_creation {
+ let mut metadata_init = metadata_init.clone();
+ metadata_init.data.insert(
+ "dapp_definitions".to_owned(),
+ KeyValueStoreInitEntry {
+ value: Some(MetadataValue::GlobalAddressArray(vec![
+ dapp_definition_account.into(),
+ ])),
+ lock: false,
+ },
+ );
+
+ keys.push(key);
+ manifest_builder = manifest_builder.create_fungible_resource(
+ OwnerRole::Updatable(rule!(require(
+ NonFungibleGlobalId::from_public_key(
+ &ephemeral_private_key.public_key()
+ )
+ ))),
+ true,
+ 0,
+ FungibleResourceRoles {
+ mint_roles: mint_roles! {
+ minter => rule!(deny_all);
+ minter_updater => rule!(deny_all);
+ },
+ burn_roles: burn_roles! {
+ burner => rule!(deny_all);
+ burner_updater => rule!(deny_all);
+ },
+ freeze_roles: freeze_roles! {
+ freezer => rule!(deny_all);
+ freezer_updater => rule!(deny_all);
+ },
+ recall_roles: recall_roles! {
+ recaller => rule!(deny_all);
+ recaller_updater => rule!(deny_all);
+ },
+ withdraw_roles: withdraw_roles! {
+ withdrawer => rule!(allow_all);
+ withdrawer_updater => rule!(deny_all);
+ },
+ deposit_roles: deposit_roles! {
+ depositor => rule!(allow_all);
+ depositor_updater => rule!(deny_all);
+ },
+ },
+ ModuleConfig {
+ roles: Default::default(),
+ init: metadata_init,
+ },
+ Some(dec!(1)),
+ )
+ }
+ let manifest = manifest_builder
+ .try_deposit_entire_worktop_or_abort(ephemeral_account, None)
+ .build();
+ let badges = keys
+ .into_iter()
+ .zip(
+ execution_service
+ .execute_manifest(manifest.clone())?
+ .new_entities
+ .new_resource_addresses,
+ )
+ .map(|(key, resource_address)| {
+ (key, (ephemeral_account, resource_address))
+ });
+
+ BadgeIndexedData::from_map(already_existing_badges.chain(badges))
+ .expect("Can't fail")
+ };
+
+ let resolved_rules = resolved_badges
+ .map(|(_, resource_address)| rule!(require(*resource_address)));
+
+ // The resources created in the previous transaction have an updatable owner
+ // role that is set to the ephemeral private key. In this transaction the
+ // owner role is modified to be the protocol owner.
+ {
+ let mut manifest_builder = ManifestBuilder::new();
+ for ((_, address), handling) in
+ resolved_badges.zip_borrowed(&configuration.badges).iter()
+ {
+ if let BadgeHandling::CreateAndSend { .. } = handling {
+ manifest_builder = manifest_builder
+ .create_proof_from_account_of_amount(
+ resolved_badges.protocol_owner_badge.0,
+ resolved_badges.protocol_owner_badge.1,
+ dec!(1),
+ )
+ .set_owner_role(
+ *address,
+ resolved_rules.protocol_owner_badge.clone(),
+ )
+ .lock_owner_role(*address);
+ }
+ }
+ let manifest = manifest_builder.build();
+
+ execution_service.execute_manifest(manifest.clone())?;
+ }
+
+ // Publishing the packages that need to be published
+ let resolved_blueprint_ids = {
+ let mut map = configuration.packages.protocol_entities.into_map();
+ map.extend(configuration.packages.exchange_adapter_entities.into_map());
+
+ let iterator = map
+ .into_iter()
+ .filter_map(|(key, package_handling)| {
+ if let PackageHandling::LoadAndPublish {
+ crate_package_name,
+ metadata,
+ blueprint_name,
+ } = package_handling
+ {
+ Some((key, (crate_package_name, metadata, blueprint_name)))
+ } else {
+ None
+ }
+ })
+ .map(|(key, (crate_package_name, metadata, blueprint_name))| {
+ let (code, definition) = PackageLoader::get(crate_package_name);
+
+ let mut metadata = metadata.clone();
+ metadata.data.insert(
+ "dapp_definition".to_owned(),
+ KeyValueStoreInitEntry {
+ value: Some(MetadataValue::GlobalAddress(
+ dapp_definition_account.into(),
+ )),
+ lock: false,
+ },
+ );
+
+ (key, (code, definition, metadata, blueprint_name.clone()))
+ })
+ .sorted_by(|x, y| x.1 .0.len().cmp(&y.1 .0.len()));
+
+ // We want to get as many packages into one transaction. Goal is to
+ // have each transaction be 980 kbs or less in size. If the addition
+ // of a package increases the size beyond that then it goes in the
+ // next batch.
+ let mut batches = vec![Vec::<(
+ String,
+ (Vec, PackageDefinition, MetadataInit, String),
+ )>::new()];
+ for (key, (code, definition, metadata_init, blueprint_name)) in iterator
+ {
+ let latest_batch = batches.last_mut().expect("Impossible!");
+ let total_code_size = latest_batch
+ .iter()
+ .map(|entry| entry.1 .0.len())
+ .sum::();
+
+ let size_if_code_is_added_to_batch = total_code_size + code.len();
+ // Add to next batch
+ if size_if_code_is_added_to_batch > 980 * 1024 {
+ batches.push(vec![(
+ key.to_owned(),
+ (code, definition, metadata_init, blueprint_name),
+ )])
+ }
+ // Add to this batch
+ else {
+ latest_batch.push((
+ key.to_owned(),
+ (code, definition, metadata_init, blueprint_name),
+ ));
+ }
+ }
+
+ // Creating transactions of the batches
+ let mut addresses_map = IndexMap::::new();
+ for batch in batches {
+ let mut manifest_builder = ManifestBuilder::new();
+ for (_, (code, definition, metadata, _)) in batch.iter() {
+ manifest_builder = manifest_builder.publish_package_advanced(
+ None,
+ code.clone(),
+ definition.clone(),
+ metadata.clone(),
+ OwnerRole::Fixed(
+ resolved_rules.protocol_owner_badge.clone(),
+ ),
+ );
+ }
+ let manifest = manifest_builder.build();
+
+ addresses_map.extend(
+ execution_service
+ .execute_manifest(manifest.clone())?
+ .new_entities
+ .new_package_addresses
+ .into_iter()
+ .zip(batch.into_iter())
+ .map(
+ |(
+ package_address,
+ (key, (_, _, _, blueprint_name)),
+ )| {
+ (
+ key,
+ BlueprintId {
+ package_address,
+ blueprint_name,
+ },
+ )
+ },
+ ),
+ );
+ }
+
+ let addresses_map = configuration
+ .packages
+ .protocol_entities
+ .into_map()
+ .into_iter()
+ .filter_map(|(key, value)| {
+ if let PackageHandling::UseExisting { package_address } = value
+ {
+ Some((key.to_owned(), package_address.clone()))
+ } else {
+ None
+ }
+ })
+ .chain(addresses_map)
+ .collect::>();
+
+ Entities {
+ protocol_entities: ProtocolIndexedData::from_map(
+ addresses_map.clone(),
+ )
+ .expect("Can't fail!"),
+ exchange_adapter_entities: ExchangeIndexedData::from_map(
+ addresses_map,
+ )
+ .expect("Can't fail!"),
+ }
+ };
+
+ // Computing the package global caller
+ let resolved_package_global_caller_rules = Entities {
+ protocol_entities: resolved_blueprint_ids.protocol_entities.map(
+ |blueprint_id| {
+ rule!(require(package_of_direct_caller(
+ blueprint_id.package_address
+ )))
+ },
+ ),
+ exchange_adapter_entities: resolved_blueprint_ids
+ .exchange_adapter_entities
+ .map(|blueprint_id| {
+ rule!(require(package_of_direct_caller(
+ blueprint_id.package_address
+ )))
+ }),
+ };
+
+ let resolved_exchange_data = ExchangeIndexedData {
+ ociswap_v2: handle_ociswap_v2_exchange_information(
+ &mut execution_service,
+ configuration.exchange_information.ociswap_v2.as_ref(),
+ dapp_definition_account,
+ &resolved_rules,
+ &resolved_package_global_caller_rules,
+ &resolved_user_resources,
+ configuration.protocol_configuration.protocol_resource,
+ &configuration.additional_information,
+ )?,
+ defiplaza_v2: handle_defiplaza_v2_exchange_information(
+ &mut execution_service,
+ configuration.exchange_information.defiplaza_v2.as_ref(),
+ dapp_definition_account,
+ &resolved_rules,
+ &resolved_package_global_caller_rules,
+ &resolved_user_resources,
+ configuration.protocol_configuration.protocol_resource,
+ )?,
+ caviarnine_v1: handle_caviarnine_v1_exchange_information(
+ &mut execution_service,
+ configuration.exchange_information.caviarnine_v1.as_ref(),
+ dapp_definition_account,
+ &resolved_rules,
+ &resolved_package_global_caller_rules,
+ &resolved_user_resources,
+ configuration.protocol_configuration.protocol_resource,
+ )?,
+ };
+
+ // Creating the adapter components of the various exchange packages that we
+ // published.
+ let resolved_adapter_component_addresses = {
+ let adapter_instantiation_instructions = resolved_blueprint_ids
+ .exchange_adapter_entities
+ .clone()
+ .zip(
+ configuration
+ .protocol_configuration
+ .entities_metadata
+ .exchange_adapter_entities
+ .clone(),
+ )
+ .map(|(adapter_package, metadata_init)| {
+ let mut metadata_init = metadata_init.clone();
+ metadata_init.data.insert(
+ "dapp_definition".to_owned(),
+ KeyValueStoreInitEntry {
+ value: Some(MetadataValue::GlobalAddress(
+ dapp_definition_account.into(),
+ )),
+ lock: false,
+ },
+ );
+
+ InstructionV1::CallFunction {
+ package_address: adapter_package.package_address.into(),
+ blueprint_name: adapter_package.blueprint_name.clone(),
+ function_name: "instantiate".to_owned(),
+ args: to_manifest_value(&(
+ resolved_rules.protocol_manager_badge.clone(),
+ resolved_rules.protocol_owner_badge.clone(),
+ metadata_init,
+ OwnerRole::Fixed(
+ resolved_rules.protocol_owner_badge.clone(),
+ ),
+ None::,
+ ))
+ .expect("Impossible!"),
+ }
+ });
+
+ let manifest = TransactionManifestV1 {
+ instructions: adapter_instantiation_instructions
+ .iter()
+ .cloned()
+ .collect(),
+ blobs: Default::default(),
+ };
+
+ ExchangeIndexedData::from_map(
+ adapter_instantiation_instructions
+ .into_map()
+ .into_iter()
+ .zip(
+ execution_service
+ .execute_manifest(manifest)?
+ .new_entities
+ .new_component_addresses,
+ )
+ .map(|((key, _), component_address)| (key, component_address)),
+ )
+ .expect("Cant fail!")
+ };
+
+ // Instantiating the oracle component
+ let oracle_component_address = {
+ let mut metadata_init = configuration
+ .protocol_configuration
+ .entities_metadata
+ .protocol_entities
+ .simple_oracle
+ .clone();
+
+ metadata_init.data.insert(
+ "dapp_definition".to_owned(),
+ KeyValueStoreInitEntry {
+ value: Some(MetadataValue::GlobalAddress(
+ dapp_definition_account.into(),
+ )),
+ lock: false,
+ },
+ );
+
+ let manifest = ManifestBuilder::new()
+ .call_function(
+ resolved_blueprint_ids
+ .protocol_entities
+ .simple_oracle
+ .package_address,
+ resolved_blueprint_ids
+ .protocol_entities
+ .simple_oracle
+ .blueprint_name
+ .clone(),
+ "instantiate",
+ (
+ resolved_rules.oracle_manager_badge.clone(),
+ metadata_init,
+ OwnerRole::Fixed(
+ resolved_rules.protocol_owner_badge.clone(),
+ ),
+ None::,
+ ),
+ )
+ .build();
+
+ execution_service
+ .execute_manifest(manifest)?
+ .new_entities
+ .new_component_addresses
+ .first()
+ .copied()
+ .unwrap()
+ };
+
+ // Instantiating the Ignition component
+ let ignition_component_address = {
+ let mut metadata_init = configuration
+ .protocol_configuration
+ .entities_metadata
+ .protocol_entities
+ .ignition
+ .clone();
+
+ metadata_init.data.insert(
+ "dapp_definition".to_owned(),
+ KeyValueStoreInitEntry {
+ value: Some(MetadataValue::GlobalAddress(
+ dapp_definition_account.into(),
+ )),
+ lock: false,
+ },
+ );
+
+ let ignition_initialization_parameters =
+ InitializationParametersManifest {
+ initial_pool_information: Some(
+ resolved_exchange_data
+ .clone()
+ .zip(resolved_adapter_component_addresses)
+ .iter()
+ .filter_map(
+ |(exchange_information, adapter_component)| {
+ exchange_information.as_ref().map(
+ |exchange_information| {
+ (
+ exchange_information
+ .blueprint_id
+ .clone(),
+ PoolBlueprintInformation {
+ adapter: *adapter_component,
+ allowed_pools:
+ exchange_information
+ .pools
+ .iter()
+ .copied()
+ .collect(),
+ liquidity_receipt:
+ exchange_information
+ .liquidity_receipt,
+ },
+ )
+ },
+ )
+ },
+ )
+ .collect(),
+ ),
+ initial_user_resource_volatility: Some(
+ resolved_user_resources
+ .zip(
+ configuration
+ .protocol_configuration
+ .user_resource_volatility,
+ )
+ .iter()
+ .map(|(address, volatility)| (*address, *volatility))
+ .collect(),
+ ),
+ initial_reward_rates: Some(
+ configuration.protocol_configuration.reward_rates.clone(),
+ ),
+ initial_volatile_protocol_resources: None,
+ initial_non_volatile_protocol_resources: None,
+ initial_is_open_position_enabled: Some(
+ configuration
+ .protocol_configuration
+ .allow_opening_liquidity_positions,
+ ),
+ initial_is_close_position_enabled: Some(
+ configuration
+ .protocol_configuration
+ .allow_closing_liquidity_positions,
+ ),
+ };
+
+ let manifest = ManifestBuilder::new()
+ .call_function(
+ resolved_blueprint_ids
+ .protocol_entities
+ .ignition
+ .package_address,
+ resolved_blueprint_ids
+ .protocol_entities
+ .ignition
+ .blueprint_name
+ .clone(),
+ "instantiate",
+ (
+ metadata_init,
+ OwnerRole::Fixed(
+ resolved_rules.protocol_owner_badge.clone(),
+ ),
+ resolved_rules.protocol_owner_badge.clone(),
+ resolved_rules.protocol_manager_badge.clone(),
+ configuration.protocol_configuration.protocol_resource,
+ oracle_component_address,
+ configuration
+ .protocol_configuration
+ .maximum_allowed_price_staleness,
+ configuration
+ .protocol_configuration
+ .maximum_allowed_price_difference_percentage,
+ ignition_initialization_parameters,
+ None::,
+ ),
+ )
+ .build();
+ execution_service
+ .execute_manifest(manifest)?
+ .new_entities
+ .new_component_addresses
+ .first()
+ .copied()
+ .unwrap()
+ };
+
+ let resolved_entity_component_addresses = Entities {
+ protocol_entities: ProtocolIndexedData {
+ ignition: ignition_component_address,
+ simple_oracle: oracle_component_address,
+ },
+ exchange_adapter_entities: resolved_adapter_component_addresses,
+ };
+
+ // Submitting the defiplaza pool pair config to the adapter
+ {
+ if let Some(ref defiplaza_v2_exchange_information) =
+ resolved_exchange_data.defiplaza_v2
+ {
+ let pair_config = execution_service
+ .with_network_connection_provider(|provider| {
+ defiplaza_v2_exchange_information.pools.try_map(|address| {
+ provider.read_component_state::(*address)
+ })
+ })?;
+
+ let pair_config_map = defiplaza_v2_exchange_information
+ .pools
+ .zip(pair_config)
+ .iter()
+ .map(|(pool_address, plaza_pair)| {
+ (*pool_address, plaza_pair.config)
+ })
+ .collect::>();
+
+ let manifest = ManifestBuilder::new()
+ .create_proof_from_account_of_amount(
+ resolved_badges.protocol_manager_badge.0,
+ resolved_badges.protocol_manager_badge.1,
+ dec!(1),
+ )
+ .call_method(
+ resolved_adapter_component_addresses.defiplaza_v2,
+ "add_pair_config",
+ (pair_config_map,),
+ )
+ .build();
+ execution_service.execute_manifest(manifest)?;
+ }
+ }
+
+ // Caching the information of the Caviarnine pools
+ {
+ if let Some(ExchangeInformation { pools, .. }) =
+ resolved_exchange_data.caviarnine_v1
+ {
+ let instructions = pools
+ .iter()
+ .map(|address| InstructionV1::CallMethod {
+ address: resolved_adapter_component_addresses
+ .caviarnine_v1
+ .into(),
+ method_name: "preload_pool_information".to_owned(),
+ args: to_manifest_value(&(*address,)).expect("Can't fail!"),
+ })
+ .collect::>();
+ let manifest = TransactionManifestV1 {
+ instructions,
+ blobs: Default::default(),
+ };
+ execution_service.execute_manifest(manifest)?;
+ }
+ }
+
+ // Setting the dApp definition metadata
+ {
+ let claimed_entities = resolved_badges
+ .iter()
+ .map(|(_, address)| GlobalAddress::from(*address))
+ .chain(resolved_blueprint_ids.exchange_adapter_entities.iter().map(
+ |blueprint_id| {
+ GlobalAddress::from(blueprint_id.package_address)
+ },
+ ))
+ .chain(resolved_blueprint_ids.protocol_entities.iter().map(
+ |blueprint_id| {
+ GlobalAddress::from(blueprint_id.package_address)
+ },
+ ))
+ .chain(resolved_exchange_data.iter().filter_map(|information| {
+ information.as_ref().map(|information| {
+ GlobalAddress::from(information.liquidity_receipt)
+ })
+ }))
+ .chain(
+ resolved_entity_component_addresses
+ .exchange_adapter_entities
+ .iter()
+ .map(|component_address| {
+ GlobalAddress::from(*component_address)
+ }),
+ )
+ .chain(
+ resolved_entity_component_addresses
+ .protocol_entities
+ .iter()
+ .map(|component_address| {
+ GlobalAddress::from(*component_address)
+ }),
+ )
+ .collect::>();
+
+ let manifest = ManifestBuilder::new()
+ .create_proof_from_account_of_amount(
+ resolved_badges.protocol_owner_badge.0,
+ resolved_badges.protocol_owner_badge.1,
+ dec!(1),
+ )
+ .set_metadata(
+ dapp_definition_account,
+ "claimed_entities",
+ claimed_entities,
+ )
+ .set_owner_role(
+ dapp_definition_account,
+ resolved_rules.protocol_owner_badge.clone(),
+ )
+ .lock_owner_role(dapp_definition_account)
+ .build();
+ execution_service.execute_manifest(manifest)?;
+ }
+
+ // Processing the additional operations specified in the publishing config
+ {
+ // Submitting prices to the oracle with (user_asset, protocol_asset)
+ // and (protocol_asset, user_asset) where the price of both is equal
+ // to one.
+ if configuration
+ .additional_operation_flags
+ .contains(AdditionalOperationFlags::SUBMIT_ORACLE_PRICES_OF_ONE)
+ {
+ let price_updates = resolved_user_resources
+ .iter()
+ .copied()
+ .flat_map(|address| {
+ [
+ (
+ address,
+ configuration
+ .protocol_configuration
+ .protocol_resource,
+ ),
+ (
+ configuration
+ .protocol_configuration
+ .protocol_resource,
+ address,
+ ),
+ ]
+ })
+ .map(|address_pair| (address_pair, dec!(1)))
+ .collect::>();
+
+ let manifest = ManifestBuilder::new()
+ .create_proof_from_account_of_amount(
+ resolved_badges.oracle_manager_badge.0,
+ resolved_badges.oracle_manager_badge.1,
+ dec!(1),
+ )
+ .call_method(
+ resolved_entity_component_addresses
+ .protocol_entities
+ .simple_oracle,
+ "set_price_batch",
+ (price_updates,),
+ )
+ .build();
+ execution_service.execute_manifest(manifest)?;
+ }
+
+ // Seeding Ignition with the initial set of XRD if requested.
+ if configuration.additional_operation_flags.contains(
+ AdditionalOperationFlags::PROVIDE_INITIAL_IGNITION_LIQUIDITY,
+ ) {
+ let total_amount_of_protocol_resource = dec!(10_000);
+ let mut manifest_builder = ManifestBuilder::new()
+ .create_proof_from_account_of_amount(
+ resolved_badges.protocol_owner_badge.0,
+ resolved_badges.protocol_owner_badge.1,
+ dec!(1),
+ );
+ if configuration.protocol_configuration.protocol_resource == XRD {
+ manifest_builder = manifest_builder.get_free_xrd_from_faucet()
+ } else {
+ manifest_builder = manifest_builder.mint_fungible(
+ configuration.protocol_configuration.protocol_resource,
+ total_amount_of_protocol_resource,
+ )
+ }
+
+ let manifest = manifest_builder
+ .take_from_worktop(
+ XRD,
+ total_amount_of_protocol_resource / 2,
+ "volatile",
+ )
+ .take_from_worktop(
+ XRD,
+ total_amount_of_protocol_resource / 2,
+ "non_volatile",
+ )
+ .with_name_lookup(|builder, _| {
+ let volatile = builder.bucket("volatile");
+ let non_volatile = builder.bucket("non_volatile");
+
+ builder
+ .call_method(
+ resolved_entity_component_addresses
+ .protocol_entities
+ .ignition,
+ "deposit_protocol_resources",
+ (volatile, common::prelude::Volatility::Volatile),
+ )
+ .call_method(
+ resolved_entity_component_addresses
+ .protocol_entities
+ .ignition,
+ "deposit_protocol_resources",
+ (
+ non_volatile,
+ common::prelude::Volatility::NonVolatile,
+ ),
+ )
+ })
+ .build();
+ execution_service.execute_manifest(manifest)?;
+ }
+
+ // Contributing initial liquidity to Ociswap if requested
+ if configuration.additional_operation_flags.contains(
+ AdditionalOperationFlags::PROVIDE_INITIAL_LIQUIDITY_TO_OCISWAP_BY_MINTING_USER_RESOURCE,
+ ) {
+ if let Some(ExchangeInformation { pools, .. }) = resolved_exchange_data.ociswap_v2 {
+ for (pool_address, user_resource_address) in
+ pools.zip_borrowed(&resolved_user_resources).iter()
+ {
+ let (pool_address, user_resource_address) =
+ (*pool_address, **user_resource_address);
+
+ let mut manifest_builder = ManifestBuilder::new();
+ if configuration.protocol_configuration.protocol_resource == XRD {
+ manifest_builder = manifest_builder.get_free_xrd_from_faucet()
+ } else {
+ manifest_builder = manifest_builder.mint_fungible(
+ configuration.protocol_configuration.protocol_resource,
+ dec!(10_000),
+ )
+ }
+ let manifest = manifest_builder
+ .mint_fungible(user_resource_address, dec!(10_000))
+ .take_all_from_worktop(
+ configuration.protocol_configuration.protocol_resource,
+ "protocol",
+ )
+ .take_all_from_worktop(user_resource_address, "user")
+ .then(|builder| {
+ let protocol_resource = builder.bucket("protocol");
+ let user_resource = builder.bucket("user");
+
+ let (x_bucket, y_bucket) =
+ if configuration.protocol_configuration.protocol_resource
+ < user_resource_address
+ {
+ (protocol_resource, user_resource)
+ } else {
+ (user_resource, protocol_resource)
+ };
+
+ builder.ociswap_v2_pool_add_liquidity(
+ pool_address,
+ -3921i32,
+ 9942i32,
+ x_bucket,
+ y_bucket,
+ )
+ })
+ .try_deposit_entire_worktop_or_abort(ephemeral_account, None)
+ .build();
+ execution_service.execute_manifest(manifest)?;
+ }
+ }
+ }
+ }
+
+ // Depositing the created badges into their accounts.
+ {
+ let mut manifest_builder = ManifestBuilder::new();
+ for ((current_holder_address, resource_address), handling) in
+ resolved_badges.zip_borrowed(&configuration.badges).iter()
+ {
+ if let BadgeHandling::CreateAndSend {
+ account_address: destination_account_address,
+ ..
+ } = handling
+ {
+ manifest_builder = manifest_builder
+ .withdraw_from_account(
+ *current_holder_address,
+ *resource_address,
+ dec!(1),
+ )
+ .try_deposit_entire_worktop_or_abort(
+ *destination_account_address,
+ None,
+ )
+ }
+ }
+ let manifest = manifest_builder.build();
+ execution_service.execute_manifest(manifest)?;
+ }
+
+ Ok(PublishingReceipt {
+ packages: Entities {
+ protocol_entities: resolved_blueprint_ids
+ .protocol_entities
+ .map(|blueprint_id| blueprint_id.package_address),
+ exchange_adapter_entities: resolved_blueprint_ids
+ .exchange_adapter_entities
+ .map(|blueprint_id| blueprint_id.package_address),
+ },
+ components: resolved_entity_component_addresses,
+ exchange_information: resolved_exchange_data.clone(),
+ protocol_configuration: ProtocolConfigurationReceipt {
+ protocol_resource: configuration
+ .protocol_configuration
+ .protocol_resource,
+ user_resource_volatility: configuration
+ .protocol_configuration
+ .user_resource_volatility,
+ reward_rates: configuration
+ .protocol_configuration
+ .reward_rates
+ .clone(),
+ allow_opening_liquidity_positions: configuration
+ .protocol_configuration
+ .allow_opening_liquidity_positions,
+ allow_closing_liquidity_positions: configuration
+ .protocol_configuration
+ .allow_closing_liquidity_positions,
+ maximum_allowed_price_staleness: configuration
+ .protocol_configuration
+ .maximum_allowed_price_staleness,
+ maximum_allowed_price_difference_percentage: configuration
+ .protocol_configuration
+ .maximum_allowed_price_difference_percentage,
+ user_resources: resolved_user_resources,
+ registered_pools: resolved_exchange_data.map(|information| {
+ information.as_ref().map(|information| information.pools)
+ }),
+ },
+ badges: resolved_badges.map(|(_, address)| *address),
+ })
+}
+
+fn handle_ociswap_v2_exchange_information(
+ execution_service: &mut ExecutionService,
+ exchange_information: Option<
+ &ExchangeInformation,
+ >,
+ dapp_definition: ComponentAddress,
+ badge_rules: &BadgeIndexedData,
+ entity_package_caller_rules: &Entities,
+ user_resources: &UserResourceIndexedData,
+ protocol_resource: ResourceAddress,
+ additional_information: &AdditionalInformation,
+) -> Result<
+ Option>,
+ ExecutionServiceError<::Error>,
+> {
+ // No ociswap registry component is passed even through it is needed.
+ let AdditionalInformation {
+ ociswap_v2_registry_component_and_dapp_definition:
+ Some((
+ ociswap_v2_registry_component,
+ ociswap_v2_dapp_definition_account,
+ )),
+ } = additional_information
+ else {
+ return Ok(None);
+ };
+
+ match exchange_information {
+ Some(exchange_information) => {
+ // Create the liquidity receipt if it needs to be created.
+ let liquidity_receipt = match exchange_information.liquidity_receipt
+ {
+ LiquidityReceiptHandling::CreateNew {
+ ref non_fungible_schema,
+ ref metadata,
+ } => handle_liquidity_receipt_creation(
+ execution_service,
+ non_fungible_schema,
+ metadata,
+ dapp_definition,
+ badge_rules,
+ entity_package_caller_rules,
+ )?,
+ LiquidityReceiptHandling::UseExisting { resource_address } => {
+ resource_address
+ }
+ };
+
+ // Creating the liquidity pools that need to be created
+ let pools =
+ exchange_information.pools.zip(*user_resources).try_map(
+ |(pool_handling, user_resource_address)| -> Result<
+ ComponentAddress,
+ ExecutionServiceError<
+ ::Error,
+ >,
+ > {
+ let (resource_x, resource_y) =
+ if *user_resource_address > protocol_resource {
+ (protocol_resource, *user_resource_address)
+ } else {
+ (*user_resource_address, protocol_resource)
+ };
+
+ match pool_handling {
+ PoolHandling::Create => {
+ let manifest = ManifestBuilder::new()
+ .call_function(
+ exchange_information
+ .blueprint_id
+ .package_address,
+ exchange_information
+ .blueprint_id
+ .blueprint_name
+ .clone(),
+ "instantiate",
+ (
+ resource_x,
+ resource_y,
+ pdec!(1.4142135624),
+ dec!(0.01),
+ dec!(0.009),
+ ociswap_v2_registry_component,
+ Vec::<(
+ ComponentAddress,
+ ManifestBucket,
+ )>::new(
+ ),
+ ociswap_v2_dapp_definition_account,
+ ),
+ )
+ .build();
+
+ Ok(execution_service
+ .execute_manifest(manifest)?
+ .new_entities
+ .new_component_addresses
+ .first()
+ .copied()
+ .unwrap())
+ }
+ PoolHandling::UseExisting { pool_address } => {
+ Ok(*pool_address)
+ }
+ }
+ },
+ )?;
+
+ Ok(Some(ExchangeInformation {
+ blueprint_id: exchange_information.blueprint_id.clone(),
+ pools,
+ liquidity_receipt,
+ }))
+ }
+ None => Ok(None),
+ }
+}
+
+fn handle_defiplaza_v2_exchange_information(
+ execution_service: &mut ExecutionService,
+ exchange_information: Option<
+ &ExchangeInformation,
+ >,
+ dapp_definition: ComponentAddress,
+ badge_rules: &BadgeIndexedData,
+ entity_package_caller_rules: &Entities,
+ user_resources: &UserResourceIndexedData,
+ protocol_resource: ResourceAddress,
+) -> Result<
+ Option>,
+ ExecutionServiceError<::Error>,
+> {
+ match exchange_information {
+ Some(exchange_information) => {
+ // Create the liquidity receipt if it needs to be created.
+ let liquidity_receipt = match exchange_information.liquidity_receipt
+ {
+ LiquidityReceiptHandling::CreateNew {
+ ref non_fungible_schema,
+ ref metadata,
+ } => handle_liquidity_receipt_creation(
+ execution_service,
+ non_fungible_schema,
+ metadata,
+ dapp_definition,
+ badge_rules,
+ entity_package_caller_rules,
+ )?,
+ LiquidityReceiptHandling::UseExisting { resource_address } => {
+ resource_address
+ }
+ };
+
+ // Creating the liquidity pools that need to be created
+ let pools =
+ exchange_information.pools.zip(*user_resources).try_map(
+ |(pool_handling, user_resource_address)| -> Result<
+ ComponentAddress,
+ ExecutionServiceError<
+ ::Error,
+ >,
+ > {
+ match pool_handling {
+ PoolHandling::Create => {
+ let manifest = ManifestBuilder::new()
+ .call_function(
+ exchange_information
+ .blueprint_id
+ .package_address,
+ exchange_information
+ .blueprint_id
+ .blueprint_name
+ .clone(),
+ "instantiate_pair",
+ (
+ OwnerRole::None,
+ user_resource_address,
+ protocol_resource,
+ PairConfig {
+ k_in: dec!(1),
+ k_out: dec!(1.5),
+ fee: dec!(0.01),
+ decay_factor: dec!(0.9995),
+ },
+ dec!(1),
+ ),
+ )
+ .build();
+
+ Ok(execution_service
+ .execute_manifest(manifest)?
+ .new_entities
+ .new_component_addresses
+ .first()
+ .copied()
+ .unwrap())
+ }
+ PoolHandling::UseExisting { pool_address } => {
+ Ok(*pool_address)
+ }
+ }
+ },
+ )?;
+
+ Ok(Some(ExchangeInformation {
+ blueprint_id: exchange_information.blueprint_id.clone(),
+ pools,
+ liquidity_receipt,
+ }))
+ }
+ None => Ok(None),
+ }
+}
+
+fn handle_caviarnine_v1_exchange_information(
+ execution_service: &mut ExecutionService,
+ exchange_information: Option<
+ &ExchangeInformation,
+ >,
+ dapp_definition: ComponentAddress,
+ badge_rules: &BadgeIndexedData,
+ entity_package_caller_rules: &Entities,
+ user_resources: &UserResourceIndexedData,
+ protocol_resource: ResourceAddress,
+) -> Result<
+ Option>,
+ ExecutionServiceError<::Error>,
+> {
+ match exchange_information {
+ Some(exchange_information) => {
+ // Create the liquidity receipt if it needs to be created.
+ let liquidity_receipt = match exchange_information.liquidity_receipt
+ {
+ LiquidityReceiptHandling::CreateNew {
+ ref non_fungible_schema,
+ ref metadata,
+ } => handle_liquidity_receipt_creation(
+ execution_service,
+ non_fungible_schema,
+ metadata,
+ dapp_definition,
+ badge_rules,
+ entity_package_caller_rules,
+ )?,
+ LiquidityReceiptHandling::UseExisting { resource_address } => {
+ resource_address
+ }
+ };
+
+ // Creating the liquidity pools that need to be created
+ let pools =
+ exchange_information.pools.zip(*user_resources).try_map(
+ |(pool_handling, user_resource_address)| -> Result<
+ ComponentAddress,
+ ExecutionServiceError<
+ ::Error,
+ >,
+ > {
+ match pool_handling {
+ PoolHandling::Create => {
+ let manifest = ManifestBuilder::new()
+ .call_function(
+ exchange_information
+ .blueprint_id
+ .package_address,
+ exchange_information
+ .blueprint_id
+ .blueprint_name
+ .clone(),
+ "new",
+ (
+ rule!(allow_all),
+ rule!(allow_all),
+ user_resource_address,
+ protocol_resource,
+ 100u32,
+ None::,
+ ),
+ )
+ .build();
+
+ Ok(execution_service
+ .execute_manifest(manifest)?
+ .new_entities
+ .new_component_addresses
+ .first()
+ .copied()
+ .unwrap())
+ }
+ PoolHandling::UseExisting { pool_address } => {
+ Ok(*pool_address)
+ }
+ }
+ },
+ )?;
+
+ Ok(Some(ExchangeInformation {
+ blueprint_id: exchange_information.blueprint_id.clone(),
+ pools,
+ liquidity_receipt,
+ }))
+ }
+ None => Ok(None),
+ }
+}
+
+fn handle_liquidity_receipt_creation(
+ execution_service: &mut ExecutionService,
+ non_fungible_schema: &NonFungibleDataSchema,
+ metadata_init: &MetadataInit,
+ dapp_definition_account: ComponentAddress,
+ badge_rules: &BadgeIndexedData,
+ entity_package_caller_rules: &Entities,
+) -> Result<
+ ResourceAddress,
+ ExecutionServiceError<::Error>,
+> {
+ // Adding the dapp definition to the metadata
+ let mut metadata_init = metadata_init.clone();
+ metadata_init.data.insert(
+ "dapp_definitions".to_owned(),
+ KeyValueStoreInitEntry {
+ value: Some(MetadataValue::GlobalAddressArray(vec![
+ dapp_definition_account.into(),
+ ])),
+ lock: false,
+ },
+ );
+
+ let manifest = ManifestBuilder::new()
+ .call_function(
+ RESOURCE_PACKAGE,
+ NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT.to_owned(),
+ NON_FUNGIBLE_RESOURCE_MANAGER_CREATE_RUID_WITH_INITIAL_SUPPLY_IDENT.to_owned(),
+ NonFungibleResourceManagerCreateRuidWithInitialSupplyManifestInput {
+ owner_role: OwnerRole::Fixed(badge_rules.protocol_owner_badge.clone()),
+ track_total_supply: true,
+ non_fungible_schema: non_fungible_schema.clone(),
+ entries: Default::default(),
+ resource_roles: NonFungibleResourceRoles {
+ // Mintable and burnable by the Ignition package and the
+ // protocol owner can update who can do that.
+ mint_roles: mint_roles! {
+ minter => entity_package_caller_rules.protocol_entities.ignition.clone();
+ minter_updater => badge_rules.protocol_owner_badge.clone();
+ },
+ burn_roles: burn_roles! {
+ burner => entity_package_caller_rules.protocol_entities.ignition.clone();
+ burner_updater => badge_rules.protocol_owner_badge.clone();
+ },
+ // The protocol owner reserves the rights to update the data
+ // of the non-fungibles as they see fit.
+ non_fungible_data_update_roles: non_fungible_data_update_roles! {
+ non_fungible_data_updater => rule!(deny_all);
+ non_fungible_data_updater_updater => badge_rules.protocol_owner_badge.clone();
+ },
+ // Everything else is deny all and can't be changed.
+ recall_roles: recall_roles! {
+ recaller => rule!(deny_all);
+ recaller_updater => rule!(deny_all);
+ },
+ freeze_roles: freeze_roles! {
+ freezer => rule!(deny_all);
+ freezer_updater => rule!(deny_all);
+ },
+ deposit_roles: deposit_roles! {
+ depositor => rule!(allow_all);
+ depositor_updater => rule!(deny_all);
+ },
+ withdraw_roles: withdraw_roles! {
+ withdrawer => rule!(allow_all);
+ withdrawer_updater => rule!(deny_all);
+ },
+ },
+ metadata: ModuleConfig {
+ init: metadata_init,
+ roles: metadata_roles! {
+ metadata_setter => badge_rules.protocol_owner_badge.clone();
+ metadata_setter_updater => badge_rules.protocol_owner_badge.clone();
+ metadata_locker => badge_rules.protocol_owner_badge.clone();
+ metadata_locker_updater => badge_rules.protocol_owner_badge.clone();
+ }
+ },
+ address_reservation: None,
+ },
+ )
+ .build();
+
+ execution_service
+ .execute_manifest(manifest)
+ .map(|new_entities| {
+ new_entities
+ .new_entities
+ .new_resource_addresses
+ .first()
+ .copied()
+ .unwrap()
+ })
+}
+
+#[derive(Debug)]
+pub enum PublishingError {
+ NetworkConnectionProviderError(E),
+ ExecutionServiceError(ExecutionServiceError),
+}
+
+impl From