Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
29 changes: 29 additions & 0 deletions modules/pallets/intents-coprocessor/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,5 +186,34 @@ mod benchmarks {
Ok(())
}

#[benchmark]
fn register_phantom_order() {
let caller: T::AccountId = whitelisted_caller();
let commitment = H256::repeat_byte(0xab);
// Realistic chain identifier bytes (e.g. b"EVM-8453")
let chain = b"EVM-8453".to_vec();

#[extrinsic_call]
_(RawOrigin::Signed(caller), commitment, chain);

// Verify the phantom order was stored
assert!(CurrentPhantomOrder::<T>::get().is_some());
}

#[benchmark]
fn set_phantom_bid_window() -> Result<(), BenchmarkError> {
let origin =
T::GovernanceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let window: u32 = 100;

#[extrinsic_call]
_(origin as T::RuntimeOrigin, window);

// Verify the window was stored
assert_eq!(PhantomBidWindow::<T>::get(), window);

Ok(())
}

impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test);
}
93 changes: 88 additions & 5 deletions modules/pallets/intents-coprocessor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ use sp_io::offchain_index;
use sp_runtime::traits::{ConstU32, Zero};
pub use weights::WeightInfo;

use types::{Bid, GatewayInfo, IntentGatewayParams, RequestKind, TokenDecimalsUpdate, TokenInfo};
use types::{
Bid, GatewayInfo, IntentGatewayParams, PhantomOrderInfo, RequestKind, TokenDecimalsUpdate,
TokenInfo,
};

// Re-export pallet items so that they can be accessed from the crate namespace.
pub use pallet::*;
Expand Down Expand Up @@ -83,6 +86,11 @@ pub mod pallet {
#[pallet::constant]
type StorageDepositFee: Get<BalanceOf<Self>>;

/// How many blocks after phantom order creation bids are accepted. Fallback when
/// the PhantomBidWindow storage value is zero.
#[pallet::constant]
type PhantomOrderBidWindowBlocks: Get<u32>;

/// Origin that can perform governance actions
type GovernanceOrigin: EnsureOrigin<Self::RuntimeOrigin>;

Expand Down Expand Up @@ -119,6 +127,17 @@ pub mod pallet {
pub type Gateways<T: Config> =
StorageMap<_, Blake2_128Concat, StateMachine, GatewayInfo, OptionQuery>;

/// The single active phantom order. Only one is recognised at a time; registering
/// a new one replaces the previous.
#[pallet::storage]
pub type CurrentPhantomOrder<T: Config> =
StorageValue<_, (H256, PhantomOrderInfo<BlockNumberFor<T>>), OptionQuery>;

/// Governance-updatable bid acceptance window for phantom orders (in blocks).
/// Falls back to PhantomOrderBidWindowBlocks when zero.
#[pallet::storage]
pub type PhantomBidWindow<T: Config> = StorageValue<_, u32, ValueQuery>;

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
Expand Down Expand Up @@ -147,6 +166,10 @@ pub mod pallet {
},
/// Storage deposit fee was updated
StorageDepositFeeUpdated { fee: BalanceOf<T> },
/// A phantom order was registered as the current active one
PhantomOrderRegistered { commitment: H256, chain: Vec<u8>, created_at: BlockNumberFor<T> },
/// The phantom order bid window was updated
PhantomBidWindowUpdated { window: u32 },
}

#[pallet::error]
Expand All @@ -163,6 +186,10 @@ pub mod pallet {
InvalidUserOp,
/// Failed to dispatch cross-chain request
DispatchFailed,
/// A bid was submitted for a phantom order after the acceptance window closed
PhantomOrderBidWindowClosed,
/// A filler already has a bid for this phantom order
DuplicatePhantomBid,
}

#[pallet::call]
Expand Down Expand Up @@ -191,6 +218,22 @@ pub mod pallet {
// Validate user_op is not empty
ensure!(!user_op.is_empty(), Error::<T>::InvalidUserOp);

// Phantom orders have stricter rules: one bid per filler, no updates, and only
// within the configured acceptance window after the order was registered.
if let Some((phantom_commitment, info)) = CurrentPhantomOrder::<T>::get() {
if commitment == phantom_commitment {
let window: BlockNumberFor<T> = Self::phantom_bid_window().into();
ensure!(
frame_system::Pallet::<T>::block_number() <= info.created_at_block + window,
Error::<T>::PhantomOrderBidWindowClosed
);
ensure!(
!Bids::<T>::contains_key(&commitment, &filler),
Error::<T>::DuplicatePhantomBid
);
}
}

// If a bid already exists, unreserve the old deposit first
if let Some(old_deposit) = Bids::<T>::get(&commitment, &filler) {
<T as Config>::Currency::unreserve(&filler, old_deposit);
Expand Down Expand Up @@ -280,10 +323,8 @@ pub mod pallet {
}

// Prepare cross-chain request to notify existing gateway
let new_deployment = types::NewDeployment {
chain: state_machine.to_string().into_bytes(),
gateway,
};
let new_deployment =
types::NewDeployment { chain: state_machine.to_string().into_bytes(), gateway };
let request = RequestKind::AddDeployment(new_deployment);
let body = request.encode_body();

Expand Down Expand Up @@ -443,6 +484,39 @@ pub mod pallet {

Ok(())
}

@Wizdave97 Wizdave97 Jun 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no extrinsic to setup the phantom order details, like token pairs, chain for the order?

/// Register a phantom order as the single active one. Replaces any previously
/// registered phantom order. Called by the intent coprocessor service.
#[pallet::call_index(7)]
#[pallet::weight(T::WeightInfo::register_phantom_order())]
pub fn register_phantom_order(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct, the phantom order should be generated by the runtime in the pallet hooks

origin: OriginFor<T>,
commitment: H256,
chain: Vec<u8>,
) -> DispatchResult {
ensure_signed(origin)?;

let created_at = frame_system::Pallet::<T>::block_number();
let info = PhantomOrderInfo { created_at_block: created_at, chain: chain.clone() };
CurrentPhantomOrder::<T>::put((commitment, info));

Self::deposit_event(Event::PhantomOrderRegistered { commitment, chain, created_at });

Ok(())
}

/// Update the phantom order bid acceptance window.
#[pallet::call_index(8)]
#[pallet::weight(T::WeightInfo::set_phantom_bid_window())]
pub fn set_phantom_bid_window(origin: OriginFor<T>, window: u32) -> DispatchResult {
T::GovernanceOrigin::ensure_origin(origin)?;

PhantomBidWindow::<T>::put(window);

Self::deposit_event(Event::PhantomBidWindowUpdated { window });

Ok(())
}
}

impl<T: Config> Pallet<T>
Expand All @@ -461,6 +535,15 @@ pub mod pallet {
}
}

pub fn phantom_bid_window() -> u32 {
let window = PhantomBidWindow::<T>::get();
if window == 0 {
T::PhantomOrderBidWindowBlocks::get()
} else {
window
}
}

/// Generate offchain storage key for a bid
pub fn offchain_bid_key(commitment: &H256, filler: &T::AccountId) -> Vec<u8> {
offchain_bid_key_raw(commitment, &filler.encode())
Expand Down
8 changes: 8 additions & 0 deletions modules/pallets/intents-coprocessor/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ pub struct GatewayInfo {
pub params: IntentGatewayParams,
}

/// Tracks the single active phantom order registered by the intent coprocessor.
#[derive(Clone, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq)]
pub struct PhantomOrderInfo<BlockNumber> {
pub created_at_block: BlockNumber,
/// Raw state machine identifier bytes (e.g. `b"EVM-8453"`).
pub chain: Vec<u8>,
}

/// A bid placed by a filler for an order
#[derive(Clone, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq)]
pub struct Bid<AccountId> {
Expand Down
25 changes: 25 additions & 0 deletions modules/pallets/intents-coprocessor/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub trait WeightInfo {
fn update_params() -> Weight;
fn sweep_dust() -> Weight;
fn update_token_decimals() -> Weight;
fn register_phantom_order() -> Weight;
fn set_phantom_bid_window() -> Weight;
}

/// Weights for pallet_intents using the Substrate node and recommended hardware.
Expand Down Expand Up @@ -105,6 +107,23 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}

/// Storage: CurrentPhantomOrder (r:0 w:1)
/// Proof Skipped: CurrentPhantomOrder (max_values: Some(1), max_size: None, mode: Measured)
fn register_phantom_order() -> Weight {
// Measured on reference hardware; re-run benchmarks after schema changes.
Weight::from_parts(20_000_000, 0)
.saturating_add(Weight::from_parts(0, 1_024))
.saturating_add(T::DbWeight::get().writes(1))
}

/// Storage: PhantomBidWindow (r:0 w:1)
/// Proof Skipped: PhantomBidWindow (max_values: Some(1), max_size: Some(4), mode: Measured)
fn set_phantom_bid_window() -> Weight {
// Measured on reference hardware; re-run benchmarks after schema changes.
Weight::from_parts(10_000_000, 0)
.saturating_add(T::DbWeight::get().writes(1))
}
}

// For backwards compatibility and tests
Expand All @@ -127,4 +146,10 @@ impl WeightInfo for () {
fn update_token_decimals() -> Weight {
Weight::from_parts(75_000_000, 0)
}
fn register_phantom_order() -> Weight {
Weight::from_parts(20_000_000, 0)
}
fn set_phantom_bid_window() -> Weight {
Weight::from_parts(10_000_000, 0)
}
}
2 changes: 2 additions & 0 deletions parachain/runtimes/gargantua/src/ismp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,14 @@ impl pallet_state_coprocessor::Config for Runtime {

parameter_types! {
pub const IntentStorageDepositFee: Balance = 100 * EXISTENTIAL_DEPOSIT;
pub const IntentPhantomOrderBidWindow: u32 = 15;
}

impl pallet_intents_coprocessor::Config for Runtime {
type Dispatcher = Ismp;
type Currency = Balances;
type StorageDepositFee = IntentStorageDepositFee;
type PhantomOrderBidWindowBlocks = IntentPhantomOrderBidWindow;
type GovernanceOrigin = EnsureRoot<AccountId>;
type WeightInfo = weights::pallet_intents_coprocessor::WeightInfo<Runtime>;
}
Expand Down
Loading