diff --git a/pallets/services/src/functions.rs b/pallets/services/src/functions.rs index 947e11a9..f2e6d80a 100644 --- a/pallets/services/src/functions.rs +++ b/pallets/services/src/functions.rs @@ -11,7 +11,8 @@ use frame_support::dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo}; use sp_core::{H160, U256}; use sp_runtime::traits::UniqueSaturatedInto; use tangle_primitives::services::{ - BlueprintManager, Field, OperatorPreferences, Service, ServiceBlueprint, + BlueprintServiceManager, Field, MasterBlueprintServiceManagerRevision, OperatorPreferences, + Service, ServiceBlueprint, }; use super::*; @@ -38,6 +39,89 @@ impl Pallet { T::PalletEVMAddress::get() } + /// Get the address of the master blueprint service manager at a given revision. + /// + /// # Parameters + /// * `revision` - The revision of the master blueprint service manager. + /// + /// # Returns + /// * `Result>` - The address of the master blueprint service manager. + /// * `Error` - The error type. + #[doc(alias = "get_master_blueprint_service_manager_address")] + pub fn mbsm_address(revision: u32) -> Result> { + MasterBlueprintServiceManagerRevisions::::get() + .get(revision as usize) + .cloned() + .ok_or(Error::::MasterBlueprintServiceManagerRevisionNotFound) + } + + /// Get the latest revision of the master blueprint service manager. + /// + /// # Returns + /// * `u32` - The latest revision of the master blueprint service manager. + #[doc(alias = "get_master_blueprint_service_manager_revision")] + pub fn mbsm_latest_revision() -> u32 { + MasterBlueprintServiceManagerRevisions::::decode_len() + .map(|len| len.saturating_sub(1) as u32) + .unwrap_or(0) + } + + /// Get the address of the master blueprint service manager for the given blueprint. + /// + /// # Parameters + /// * `blueprint` - The service blueprint. + /// + /// # Returns + /// * `Result>` - The address of the master blueprint service manager. + /// * `Error` - The error type. + pub fn mbsm_address_of(blueprint: &ServiceBlueprint) -> Result> { + match blueprint.master_manager_revision { + MasterBlueprintServiceManagerRevision::Latest => { + Self::mbsm_address(Self::mbsm_latest_revision()) + }, + MasterBlueprintServiceManagerRevision::Specific(rev) => Self::mbsm_address(rev), + other => unimplemented!("Got unexpected case for {:?}", other), + } + } + + pub fn on_blueprint_created_hook( + blueprint: &ServiceBlueprint, + blueprint_id: u64, + owner: &T::AccountId, + ) -> Result<(bool, Weight), DispatchErrorWithPostInfo> { + let mbsm = Self::mbsm_address_of(blueprint)?; + #[allow(deprecated)] + let call = ethabi::Function { + name: String::from("onBlueprintCreated"), + inputs: vec![ + ethabi::Param { + name: String::from("blueprintId"), + kind: ethabi::ParamType::Uint(64), + internal_type: None, + }, + ethabi::Param { + name: String::from("owner"), + kind: ethabi::ParamType::Address, + internal_type: None, + }, + ServiceBlueprint::::to_ethabi_param(), + ], + outputs: Default::default(), + constant: None, + state_mutability: ethabi::StateMutability::Payable, + }; + + let blueprint_id = Token::Uint(ethabi::Uint::from(blueprint_id)); + let owner = Token::Address(T::EvmAddressMapping::into_address(owner.clone())); + let blueprint = blueprint.to_ethabi(); + let data = call + .encode_input(&[blueprint_id, owner, blueprint]) + .map_err(|_| Error::::EVMAbiEncode)?; + let gas_limit = 300_000; + let info = Self::evm_call(Self::address(), mbsm, 0.into(), data, gas_limit)?; + Ok((info.exit_reason.is_succeed(), Self::weight_from_call_info(&info))) + } + /// Hook to be called upon a new operator registration on a blueprint. /// /// This function is called when a service is registered. It performs an EVM call @@ -59,7 +143,7 @@ impl Pallet { value: BalanceOf, ) -> Result<(bool, Weight), DispatchErrorWithPostInfo> { let (allowed, weight) = match blueprint.manager { - BlueprintManager::Evm(contract) => { + BlueprintServiceManager::Evm(contract) => { #[allow(deprecated)] let call = ethabi::Function { name: String::from("onRegister"), @@ -111,7 +195,7 @@ impl Pallet { prefrences: &OperatorPreferences, ) -> Result<(bool, Weight), DispatchErrorWithPostInfo> { match blueprint.manager { - BlueprintManager::Evm(contract) => { + BlueprintServiceManager::Evm(contract) => { #[allow(deprecated)] let call = ethabi::Function { name: String::from("onUnregister"), @@ -149,7 +233,7 @@ impl Pallet { prefrences: &OperatorPreferences, ) -> Result<(bool, Weight), DispatchErrorWithPostInfo> { match blueprint.manager { - BlueprintManager::Evm(contract) => { + BlueprintServiceManager::Evm(contract) => { #[allow(deprecated)] let call = ethabi::Function { name: String::from("onUpdatePriceTargets"), @@ -191,7 +275,7 @@ impl Pallet { restaking_percent: u8, ) -> Result<(bool, Weight), DispatchErrorWithPostInfo> { match blueprint.manager { - BlueprintManager::Evm(contract) => { + BlueprintServiceManager::Evm(contract) => { #[allow(deprecated)] let call = ethabi::Function { name: String::from("onApprove"), @@ -248,7 +332,7 @@ impl Pallet { request_id: u64, ) -> Result<(bool, Weight), DispatchErrorWithPostInfo> { match blueprint.manager { - BlueprintManager::Evm(contract) => { + BlueprintServiceManager::Evm(contract) => { #[allow(deprecated)] let call = ethabi::Function { name: String::from("onReject"), @@ -312,7 +396,7 @@ impl Pallet { value: BalanceOf, ) -> Result<(bool, Weight), DispatchErrorWithPostInfo> { let (allowed, weight) = match blueprint.manager { - BlueprintManager::Evm(contract) => { + BlueprintServiceManager::Evm(contract) => { #[allow(deprecated)] let call = ethabi::Function { name: String::from("onRequest"), @@ -425,7 +509,7 @@ impl Pallet { ttl: BlockNumberFor, ) -> Result<(bool, Weight), DispatchErrorWithPostInfo> { let (allowed, weight) = match blueprint.manager { - BlueprintManager::Evm(contract) => { + BlueprintServiceManager::Evm(contract) => { #[allow(deprecated)] let call = ethabi::Function { name: String::from("onServiceInitialized"), @@ -514,7 +598,7 @@ impl Pallet { owner: &T::AccountId, ) -> Result<(bool, Weight), DispatchErrorWithPostInfo> { let (allowed, weight) = match blueprint.manager { - BlueprintManager::Evm(contract) => { + BlueprintServiceManager::Evm(contract) => { #[allow(deprecated)] let call = ethabi::Function { name: String::from("onServiceTermination"), @@ -572,7 +656,7 @@ impl Pallet { inputs: &[Field], ) -> Result<(bool, Weight), DispatchErrorWithPostInfo> { let (allowed, weight) = match blueprint.manager { - BlueprintManager::Evm(contract) => { + BlueprintServiceManager::Evm(contract) => { #[allow(deprecated)] let call = ethabi::Function { name: String::from("onJobCall"), @@ -647,7 +731,7 @@ impl Pallet { outputs: &[Field], ) -> Result<(bool, Weight), DispatchErrorWithPostInfo> { let (allowed, weight) = match blueprint.manager { - BlueprintManager::Evm(contract) => { + BlueprintServiceManager::Evm(contract) => { #[allow(deprecated)] let call = ethabi::Function { name: String::from("onJobResult"), diff --git a/pallets/services/src/lib.rs b/pallets/services/src/lib.rs index 1ddf855a..0f084836 100644 --- a/pallets/services/src/lib.rs +++ b/pallets/services/src/lib.rs @@ -62,6 +62,7 @@ pub mod module { use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize}; use sp_runtime::Percent; use sp_std::vec::Vec; + use tangle_primitives::services::MasterBlueprintServiceManagerRevision; use tangle_primitives::{ services::{PriceTargets, *}, MultiAssetDelegationInfo, @@ -161,6 +162,12 @@ pub mod module { /// Maximum number of assets per service. #[pallet::constant] type MaxAssetsPerService: Get + Default + Parameter + MaybeSerializeDeserialize; + /// Maximum number of versions of Master Blueprint Service Manager allowed. + #[pallet::constant] + type MaxMasterBlueprintServiceManagerVersions: Get + + Default + + Parameter + + MaybeSerializeDeserialize; /// The constraints for the service module. /// use [crate::types::ConstraintsOf] with `Self` to implement this trait. @@ -202,6 +209,8 @@ pub mod module { pub enum Error { /// The service blueprint was not found. BlueprintNotFound, + /// Blueprint creation is interrupted. + BlueprintCreationInterrupted, /// The caller is already registered as a operator. AlreadyRegistered, /// The caller does not have the requirements to be a operator. @@ -274,6 +283,8 @@ pub mod module { NoDisputeOrigin, /// The Unapplied Slash are not found. UnappliedSlashNotFound, + /// The Supplied Master Blueprint Service Manager Revision is not found. + MasterBlueprintServiceManagerRevisionNotFound, } #[pallet::event] @@ -589,6 +600,14 @@ pub mod module { ResultQuery::UnappliedSlashNotFound>, >; + /// All the Master Blueprint Service Managers revisions. + /// + /// Where the index is the revision number. + #[pallet::storage] + #[pallet::getter(fn mbsm_revisions)] + pub type MasterBlueprintServiceManagerRevisions = + StorageValue<_, BoundedVec, ValueQuery>; + // *** auxiliary storage and maps *** #[pallet::storage] #[pallet::getter(fn operator_profile)] @@ -613,15 +632,38 @@ pub mod module { #[pallet::weight(T::WeightInfo::create_blueprint())] pub fn create_blueprint( origin: OriginFor, - blueprint: ServiceBlueprint, - ) -> DispatchResult { + mut blueprint: ServiceBlueprint, + ) -> DispatchResultWithPostInfo { let owner = ensure_signed(origin)?; let blueprint_id = Self::next_blueprint_id(); + // Ensure the master blueprint service manager exists and if it uses + // latest, pin it to the latest revision. + match blueprint.master_manager_revision { + MasterBlueprintServiceManagerRevision::Latest => { + let latest_revision = Self::mbsm_latest_revision(); + blueprint.master_manager_revision = + MasterBlueprintServiceManagerRevision::Specific(latest_revision); + }, + MasterBlueprintServiceManagerRevision::Specific(rev) => { + ensure!( + rev <= Self::mbsm_latest_revision(), + Error::::MasterBlueprintServiceManagerRevisionNotFound, + ); + }, + _ => unreachable!("MasterBlueprintServiceManagerRevision case is not implemented"), + }; + + let (allowed, _weight) = + Self::on_blueprint_created_hook(&blueprint, blueprint_id, &owner)?; + + ensure!(allowed, Error::::BlueprintCreationInterrupted); + Blueprints::::insert(blueprint_id, (owner.clone(), blueprint)); NextBlueprintId::::set(blueprint_id.saturating_add(1)); Self::deposit_event(Event::BlueprintCreated { owner, blueprint_id }); - Ok(()) + // TODO: update weight for the creation of the blueprint. + Ok(PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes }) } /// Pre-register the caller as an operator for a specific blueprint. diff --git a/pallets/services/src/mock.rs b/pallets/services/src/mock.rs index f54f0efd..ac4b90fb 100644 --- a/pallets/services/src/mock.rs +++ b/pallets/services/src/mock.rs @@ -366,6 +366,10 @@ parameter_types! { #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub const SlashDeferDuration: u32 = 7; + + #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] + pub const MaxMasterBlueprintServiceManagerRevisions: u32 = u32::MAX; } impl Config for Runtime { @@ -397,6 +401,7 @@ impl Config for Runtime { type MaxContainerImageNameLength = MaxContainerImageNameLength; type MaxContainerImageTagLength = MaxContainerImageTagLength; type MaxAssetsPerService = MaxAssetsPerService; + type MaxMasterBlueprintServiceManagerVersions = MaxMasterBlueprintServiceManagerRevisions; type Constraints = pallet_services::types::ConstraintsOf; type OperatorDelegationManager = MockDelegationManager; type SlashDeferDuration = SlashDeferDuration; diff --git a/pallets/services/src/tests.rs b/pallets/services/src/tests.rs index 8a8c1c26..90423e60 100644 --- a/pallets/services/src/tests.rs +++ b/pallets/services/src/tests.rs @@ -73,25 +73,21 @@ fn cggmp21_blueprint() -> ServiceBlueprint> { #[allow(deprecated)] ServiceBlueprint { metadata: ServiceMetadata { name: "CGGMP21 TSS".try_into().unwrap(), ..Default::default() }, - manager: BlueprintManager::Evm(CGGMP21_BLUEPRINT), + manager: BlueprintServiceManager::Evm(CGGMP21_BLUEPRINT), + master_manager_revision: MasterBlueprintServiceManagerRevision::Latest, jobs: bounded_vec![ JobDefinition { metadata: JobMetadata { name: "keygen".try_into().unwrap(), ..Default::default() }, params: bounded_vec![FieldType::Uint8], result: bounded_vec![FieldType::Bytes], - verifier: JobResultVerifier::Evm(CGGMP21_BLUEPRINT), }, JobDefinition { metadata: JobMetadata { name: "sign".try_into().unwrap(), ..Default::default() }, params: bounded_vec![FieldType::Uint64, FieldType::Bytes], result: bounded_vec![FieldType::Bytes], - #[allow(deprecated)] - verifier: JobResultVerifier::Evm(CGGMP21_BLUEPRINT), }, ], - registration_hook: ServiceRegistrationHook::Evm(CGGMP21_BLUEPRINT), registration_params: bounded_vec![], - request_hook: ServiceRequestHook::Evm(CGGMP21_BLUEPRINT), request_params: bounded_vec![], gadget: Default::default(), } diff --git a/precompiles/services/src/mock.rs b/precompiles/services/src/mock.rs index 9a203e1e..aa7be4ec 100644 --- a/precompiles/services/src/mock.rs +++ b/precompiles/services/src/mock.rs @@ -473,6 +473,10 @@ parameter_types! { #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub const MaxAssetsPerService: u32 = 164; + #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] + pub const MaxMasterBlueprintServiceManagerRevisions: u32 = u32::MAX; + #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub const SlashDeferDuration: u32 = 7; @@ -507,6 +511,7 @@ impl pallet_services::Config for Runtime { type MaxContainerImageNameLength = MaxContainerImageNameLength; type MaxContainerImageTagLength = MaxContainerImageTagLength; type MaxAssetsPerService = MaxAssetsPerService; + type MaxMasterBlueprintServiceManagerVersions = MaxMasterBlueprintServiceManagerRevisions; type Constraints = pallet_services::types::ConstraintsOf; type OperatorDelegationManager = MockDelegationManager; type SlashDeferDuration = SlashDeferDuration; diff --git a/precompiles/services/src/tests.rs b/precompiles/services/src/tests.rs index 5ec9d03f..ecd02266 100644 --- a/precompiles/services/src/tests.rs +++ b/precompiles/services/src/tests.rs @@ -12,11 +12,12 @@ use sp_core::ecdsa; use sp_core::{H160, U256}; use sp_runtime::bounded_vec; use sp_runtime::AccountId32; -use tangle_primitives::services::BlueprintManager; +use tangle_primitives::services::BlueprintServiceManager; use tangle_primitives::services::FieldType; use tangle_primitives::services::JobDefinition; use tangle_primitives::services::JobMetadata; use tangle_primitives::services::JobResultVerifier; +use tangle_primitives::services::MasterBlueprintServiceManagerRevision; use tangle_primitives::services::PriceTargets; use tangle_primitives::services::ServiceMetadata; use tangle_primitives::services::ServiceRegistrationHook; @@ -64,25 +65,22 @@ fn cggmp21_blueprint() -> ServiceBlueprint> { #[allow(deprecated)] ServiceBlueprint { metadata: ServiceMetadata { name: "CGGMP21 TSS".try_into().unwrap(), ..Default::default() }, - manager: BlueprintManager::Evm(CGGMP21_BLUEPRINT), + manager: BlueprintServiceManager::Evm(CGGMP21_BLUEPRINT), + master_manager_revision: MasterBlueprintServiceManagerRevision::Latest, jobs: bounded_vec![ JobDefinition { metadata: JobMetadata { name: "keygen".try_into().unwrap(), ..Default::default() }, params: bounded_vec![FieldType::Uint8], result: bounded_vec![FieldType::Bytes], - verifier: JobResultVerifier::Evm(CGGMP21_BLUEPRINT), }, JobDefinition { metadata: JobMetadata { name: "sign".try_into().unwrap(), ..Default::default() }, params: bounded_vec![FieldType::Uint64, FieldType::Bytes], result: bounded_vec![FieldType::Bytes], #[allow(deprecated)] - verifier: JobResultVerifier::Evm(CGGMP21_BLUEPRINT), }, ], - registration_hook: ServiceRegistrationHook::Evm(CGGMP21_BLUEPRINT), registration_params: bounded_vec![], - request_hook: ServiceRequestHook::Evm(CGGMP21_BLUEPRINT), request_params: bounded_vec![], gadget: Default::default(), } diff --git a/primitives/src/services/mod.rs b/primitives/src/services/mod.rs index 7757ce25..a70cb796 100644 --- a/primitives/src/services/mod.rs +++ b/primitives/src/services/mod.rs @@ -108,9 +108,6 @@ pub struct JobDefinition { /// These are the result, the return values of this job. /// i.e. the output. pub result: BoundedVec, - /// The verifier of the job result. - #[deprecated(note = "Use `blueprint.manager` instead.")] - pub verifier: JobResultVerifier, } #[derive(Educe, Encode, Decode, TypeInfo, MaxEncodedLen)] @@ -301,16 +298,16 @@ pub enum ServiceRequestHook { Evm(sp_core::H160), } -/// Service Blueprint Manager is a smart contract that will manage the service lifecycle. +/// Blueprint Service Manager is a smart contract that will manage the service lifecycle. #[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Clone, Copy, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[non_exhaustive] -pub enum BlueprintManager { +pub enum BlueprintServiceManager { /// A Smart contract that will manage the service lifecycle. Evm(sp_core::H160), } -impl BlueprintManager { +impl BlueprintServiceManager { pub fn try_into_evm(self) -> Result { match self { Self::Evm(addr) => Ok(addr), @@ -318,12 +315,33 @@ impl BlueprintManager { } } -impl Default for BlueprintManager { +impl Default for BlueprintServiceManager { fn default() -> Self { Self::Evm(Default::default()) } } +/// Master Blueprint Service Manager Revision. +#[derive( + Default, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Clone, Copy, MaxEncodedLen, +)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum MasterBlueprintServiceManagerRevision { + /// Use Whatever the latest revision available on-chain. + /// + /// This is the default value. + #[default] + #[codec(index = 0)] + Latest, + + /// Use a specific revision number. + /// + /// Note: Must be already deployed on-chain. + #[codec(index = 1)] + Specific(u32), +} + #[derive(Educe, Encode, Decode, TypeInfo, MaxEncodedLen)] #[educe(Default(bound()), Debug(bound()), Clone(bound()), PartialEq(bound()), Eq)] #[scale_info(skip_type_params(C))] @@ -367,18 +385,18 @@ pub struct ServiceBlueprint { pub metadata: ServiceMetadata, /// The job definitions that are available in this service. pub jobs: BoundedVec, C::MaxJobsPerService>, - /// The registration hook that will be called before restaker registration. - #[deprecated(note = "Use `manager` instead.")] - pub registration_hook: ServiceRegistrationHook, /// The parameters that are required for the service registration. pub registration_params: BoundedVec, /// The request hook that will be called before creating a service from the service blueprint. - #[deprecated(note = "Use `manager` instead.")] - pub request_hook: ServiceRequestHook, /// The parameters that are required for the service request. pub request_params: BoundedVec, - /// A Blueprint Manager is a smart contract that implements the `BlueprintManager` interface. - pub manager: BlueprintManager, + /// A Blueprint Manager is a smart contract that implements the `IBlueprintServiceManager` interface. + pub manager: BlueprintServiceManager, + /// The Revision number of the Master Blueprint Service Manager. + /// + /// If not sure what to use, use `MasterBlueprintServiceManagerRevision::default()` which will use + /// the latest revision available. + pub master_manager_revision: MasterBlueprintServiceManagerRevision, /// The gadget that will be executed for the service. pub gadget: Gadget, } @@ -399,6 +417,112 @@ impl ServiceBlueprint { ) -> Result<(), TypeCheckError> { type_checker(&self.request_params, args) } + + /// Converts the struct to ethabi ParamType. + pub fn to_ethabi_param_type() -> ethabi::ParamType { + ethabi::ParamType::Tuple(vec![ + // Service Metadata + ethabi::ParamType::Tuple(vec![ + // Service Name + ethabi::ParamType::String, + // Service Description + ethabi::ParamType::String, + // Service Author + ethabi::ParamType::String, + // Service Category + ethabi::ParamType::String, + // Code Repository + ethabi::ParamType::String, + // Service Logo + ethabi::ParamType::String, + // Service Website + ethabi::ParamType::String, + // Service License + ethabi::ParamType::String, + ]), + // Job Definitions ? + // Registration Parameters ? + // Request Parameters ? + // Blueprint Manager + ethabi::ParamType::Address, + // Master Manager Revision + ethabi::ParamType::Uint(32), + // Gadget ? + ]) + } + + /// Converts the struct to ethabi Param. + pub fn to_ethabi_param() -> ethabi::Param { + ethabi::Param { + name: String::from("blueprint"), + kind: Self::to_ethabi_param_type(), + internal_type: Some(String::from("struct MasterBlueprintServiceManager.Blueprint")), + } + } + + /// Converts the struct to ethabi Token. + pub fn to_ethabi(&self) -> ethabi::Token { + ethabi::Token::Tuple(vec![ + // Service Metadata + ethabi::Token::Tuple(vec![ + // Service Name + ethabi::Token::String(self.metadata.name.as_str().into()), + // Service Description + ethabi::Token::String( + self.metadata + .description + .as_ref() + .map(|v| v.as_str().into()) + .unwrap_or_default(), + ), + // Service Author + ethabi::Token::String( + self.metadata.author.as_ref().map(|v| v.as_str().into()).unwrap_or_default(), + ), + // Service Category + ethabi::Token::String( + self.metadata.category.as_ref().map(|v| v.as_str().into()).unwrap_or_default(), + ), + // Code Repository + ethabi::Token::String( + self.metadata + .code_repository + .as_ref() + .map(|v| v.as_str().into()) + .unwrap_or_default(), + ), + // Service Logo + ethabi::Token::String( + self.metadata.logo.as_ref().map(|v| v.as_str().into()).unwrap_or_default(), + ), + // Service Website + ethabi::Token::String( + self.metadata.website.as_ref().map(|v| v.as_str().into()).unwrap_or_default(), + ), + // Service License + ethabi::Token::String( + self.metadata.license.as_ref().map(|v| v.as_str().into()).unwrap_or_default(), + ), + ]), + // Job Definitions ? + // Registration Parameters ? + // Request Parameters ? + // Blueprint Manager + match self.manager { + BlueprintServiceManager::Evm(addr) => ethabi::Token::Address(addr), + }, + // Master Manager Revision + match self.master_manager_revision { + MasterBlueprintServiceManagerRevision::Latest => { + ethabi::Token::Uint(ethabi::Uint::MAX) + }, + MasterBlueprintServiceManagerRevision::Specific(rev) => { + ethabi::Token::Uint(rev.into()) + }, + }, + // Gadget ? + ]) + } } /// A service request is a request to create a service from a service blueprint.