The goal of this task is to allow the providers to specify how are their base pricing for their machine would be specified on-chain. Looking at Akash way for configuring the bidding pricing it looks very clear for us to do something a bit similar:
Update OperatorPreferences
The current OperatorPreferences looks like the following:
pub struct OperatorPreferences {
/// The operator ECDSA public key.
pub key: ecdsa::Public,
/// The approval prefrence of the operator.
pub approval: ApprovalPrefrence,
}
We will update it by adding a new field for the PriceTargets
/// Represents the pricing structure for various hardware resources.
/// All prices are specified in USD/hr, calculated based on the average block time.
#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct PriceTargets {
/// Price per vCPU per hour
pub cpu: u64,
/// Price per MB of memory per hour
pub mem: u64,
/// Price per GB of HDD storage per hour
pub storage_hd: u64,
/// Price per GB of SSD storage per hour
pub storage_ssd: u64,
/// Price per GB of NVMe storage per hour
pub storage_nvme: u64,
}
/// Represents the preferences and configuration for an operator.
#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct OperatorPreferences {
/// The operator ECDSA public key.
pub key: ecdsa::Public,
/// The approval preference of the operator.
pub approval: ApprovalPreference,
/// The pricing targets for the operator's resources.
pub price_targets: PriceTargets,
}
An Extrinsic to Update Pricing
We will create an extrinsic in the Service Pallet to Allow for Updating the price targets with a new value
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Update the price targets for an operator.
#[pallet::weight(T::WeightInfo::update_price_targets())]
pub fn update_price_targets(
origin: OriginFor<T>,
new_price_targets: PriceTargets,
) -> DispatchResult {
let who = ensure_signed(origin)?;
// Ensure the caller is a registered operator
ensure!(Operators::<T>::contains_key(&who), Error::<T>::NotAnOperator);
// Update the price targets
Operators::<T>::mutate(&who, |operator| {
if let Some(preferences) = operator {
preferences.price_targets = new_price_targets;
}
});
// Emit an event
Self::deposit_event(Event::PriceTargetsUpdated { operator: who, new_price_targets });
Ok(())
}
}
Price Calculation
To calculate the price for a specific configuration:
Determine the number of blocks per hour based on the average block time:
Sum up the prices to get the total price per block:
let total_price_per_block = cpu_price + mem_price + storage_price;
Additional Notes
All prices are specified in USD/hr and converted to a per-block basis using the average block time of 6 seconds.
The pricing calculation uses integer arithmetic to avoid floating-point operations. The results are in millionths of a USD to maintain precision.
We would want to add limits or sanity checks on the price values to prevent extreme or unreasonable pricing.
Open Questions and Recommendations
Pricing Changes and Running Services:
Recommendation: Apply pricing changes only to newly created services. This ensures price stability for running services and prevents unexpected cost changes for users.
Implementation: Store the agreed-upon price with each service when it's created. Use this stored price for billing throughout the service's lifecycle.
Timing of Pricing Changes:
Recommendation: Implement a delayed pricing change mechanism.
Implementation:
When an operator updates their pricing, set a future block number for the change to take effect.
Store both current and future pricing in the OperatorPreferences struct.
At the specified block, automatically switch to the new pricing for new services.
This approach provides transparency and allows users to prepare for upcoming price changes.
Additional Considerations:
Implement a minimum notice period for price increases (e.g., 24 hours or 14,400 blocks at 6-second block time).
Allow immediate application of price decreases to benefit users.
Consider adding a maximum rate of change for prices to prevent drastic fluctuations.
By addressing these open questions, we can create a fair and transparent pricing system that balances the needs of both operators and users.
Overview
The goal of this task is to allow the providers to specify how are their base pricing for their machine would be specified on-chain. Looking at Akash way for configuring the bidding pricing it looks very clear for us to do something a bit similar:
Update
OperatorPreferences
The current
OperatorPreferences
looks like the following:We will update it by adding a new field for the
PriceTargets
An Extrinsic to Update Pricing
We will create an extrinsic in the Service Pallet to Allow for Updating the price targets with a new value
Price Calculation
To calculate the price for a specific configuration:
Determine the number of blocks per hour based on the average block time:
Calculate the price for each resource:
Sum up the prices to get the total price per block:
Additional Notes
Open Questions and Recommendations
Pricing Changes and Running Services:
Timing of Pricing Changes:
OperatorPreferences
struct.Additional Considerations:
By addressing these open questions, we can create a fair and transparent pricing system that balances the needs of both operators and users.