Skip to content

XCM Implementation for IP-Onchain Pallet

Overview

The IP-Onchain pallet implements XCM (Cross-Consensus Messaging) functionality to enable cross-chain transfer of intellectual property entities between parachains in the Polkadot ecosystem. This allows entities to be "wrapped" and transferred to foreign parachains while maintaining their ownership and metadata.

Architecture

Core Components

The XCM implementation consists of three main components:

  1. ForeignMover Trait - Defines the interface for sending entities across chains
  2. Foreign Request System - Manages the workflow of cross-chain entity transfers
  3. XCM Message Construction - Handles the actual XCM message building and sending

Data Structures

ForeignInfo

Located in pallets/pallet-ip-onchain/src/xcm.rs:18

pub struct ForeignInfo<AccountId, EntityId, AuthorityId, Location, ForeignAuthorityId> {
    pub who: AccountId,                           // Requester's account
    pub here_authority_id: AuthorityId,           // Authority ID on current chain
    pub entity_id: EntityId,                       // Entity being transferred
    pub foreign_location: Location,                // Destination parachain location
    pub foreign_authority_id: ForeignAuthorityId,  // Authority ID on target chain
    pub approval: bool,                            // Approval status
    pub done: bool,                                // Completion status
}

ForeignMover Trait

Located in pallets/pallet-ip-onchain/src/xcm.rs:28

pub trait ForeignMover<EntityDetails, AuthorityId, ForeignAuthorityId, ForeignRequest> {
    fn send_wrapped(
        from: staging_xcm::v5::Location,
        entity_details: EntityDetails,
        foreign_request: ForeignRequest,
    ) -> DispatchResult;

    fn covert_authority_id(id: ForeignAuthorityId) -> AuthorityId;
}

Storage

The pallet maintains two key storage items for XCM operations:

  • ForeignsRequests - Stores foreign transfer requests indexed by ForeignRequestId
  • EntitiesPointer - Tracks the origin of foreign entities (source location + original entity ID)
  • NextForeignRequests - Counter for generating unique foreign request IDs

Cross-Chain Transfer Workflow

Step 1: Create Foreign Authority Request

Extrinsic: foreign_authority_request

Location: pallets/pallet-ip-onchain/src/lib.rs:525

pub fn foreign_authority_request(
    origin: OriginFor<T>,
    foreign_authority_id: T::ForeignAuthorityId,
    name: BoundedVec<u8, T::MaxShortStringLength>,
    entity_id: T::EntityId,
    foreign_location: staging_xcm::v5::Location,
) -> DispatchResult

Process:

  1. User initiates a request to transfer an entity to another parachain
  2. A new Foreign type authority is automatically created on the local chain
  3. A foreign request is stored with approval: false and done: false
  4. Event ForeignRequestsAdded is emitted

Implementation: pallets/pallet-ip-onchain/src/xcm.rs:56

Step 2: Approve Foreign Request

Extrinsic: foreign_authority_request_approve

Location: pallets/pallet-ip-onchain/src/lib.rs:547

pub fn foreign_authority_request_approve(
    origin: OriginFor<T>,
    entity_id: T::EntityId,
    foreign_request_id: T::ForeignRequestId,
) -> DispatchResult

Process:

  1. Entity owner approves the transfer request
  2. Access control checks ensure the caller has WrapEntity permission
  3. Foreign request is marked as approval: true
  4. Event EntityWraped is emitted

Implementation: pallets/pallet-ip-onchain/src/xcm.rs:115

Step 3: Execute Transfer (Take Foreign Request)

Extrinsic: foreign_authority_request_take

Location: pallets/pallet-ip-onchain/src/lib.rs:558

pub fn foreign_authority_request_take(
    origin: OriginFor<T>,
    foreign_request_id: T::ForeignRequestId,
    src: staging_xcm::v5::Location,
) -> DispatchResult

Process:

  1. Original requester executes the approved transfer
  2. Validation checks:
  • Requester matches original who account
  • Request is approved (approval == true)
  • Request not already completed (done != true)
  1. Entity ownership is updated to the foreign authority
  2. XCM message is sent via ForeignMover::send_wrapped()
  3. Request is marked as done: true
  4. Events EntityMovedForeign and ForeignRequestsDone are emitted

Implementation: pallets/pallet-ip-onchain/src/xcm.rs:170

Step 4: Access Wrapped Entity (Destination Chain)

Extrinsic: access_wrapped_entity

Location: pallets/pallet-ip-onchain/src/lib.rs:569

pub fn access_wrapped_entity(
    origin: OriginFor<T>,
    from: staging_xcm::v5::Location,
    entity: EntityDetailsFor<T, I>,
    foreign_request: ForeignInfoFor<T, I>,
) -> DispatchResult

Process:

  1. Receives entity data from source parachain via XCM
  2. Converts foreign authority ID to local authority ID
  3. Creates a new entity with a new entity ID
  4. Stores pointer to original location and entity ID
  5. Event EntityAdded is emitted

Implementation: pallets/pallet-ip-onchain/src/xcm.rs:246

XCM Configuration

Located in runtime/src/configs/xcm_config.rs

Key Components

  • XcmOriginToTransactDispatchOrigin - Converts XCM origins to dispatch origins, including SovereignSignedViaLocation
  • Barrier - Allows unpaid execution from all sources for this use case
  • LocationToAccountId - Converts XCM locations to AccountIds for sovereign accounts

Barrier Configuration

pub type Barrier = TrailingSetTopicAsId<
    DenyThenTry<
        DenyReserveTransferToRelayChain,
        (
            TakeWeightCredit,
            WithComputedOrigin<
                (
                    AllowTopLevelPaidExecutionFrom<Everything>,
                    AllowExplicitUnpaidExecutionFrom<Everything>,  // Allows unpaid XCM
                    AllowSubscriptionsFrom<Everything>,
                ),
                UniversalLocation,
                ConstU32<8>,
            >,
        ),
    >,
>;

Access Control

The system implements several permission checks:

  1. WrapEntity Permission - Required to approve foreign requests
  2. Request Ownership - Only the original requester can execute the transfer
  3. Approval Status - Requests must be approved before execution
  4. One-time Execution - Requests cannot be executed multiple times

Events

Event Description Emitted When
ForeignRequestsAdded New foreign request created User initiates transfer request
EntityWraped Entity approved for wrapping Owner approves the transfer
EntityMovedForeign Entity transferred to foreign chain Transfer is executed
ForeignRequestsDone Foreign request completed Transfer finishes successfully
EntityAdded New entity created Entity received on destination chain

Errors

Error Description
ForeignIdIncrementFailed Failed to generate new foreign request ID
ForeignAlreadyExists Foreign request ID already exists
ForeignNotFound Foreign request not found
ForeignRequestNoPermission Caller is not the request owner
ForeignRequestNotApproved Request has not been approved
ForeignRequestAlreadyDone Request has already been executed

Usage Example

Scenario: Transfer Entity from Parachain A to Parachain B

On Parachain A (Source):

// Step 1: Create foreign authority request
IPOnchain::foreign_authority_request(
    origin,
    foreign_authority_id: 123,          // Authority ID on Parachain B
    name: b"Foreign Authority".to_vec(),
    entity_id: 456,                     // Entity to transfer
    foreign_location: Location {         // Parachain B location
        parents: 1,
        interior: [Parachain(2000)].into()
    }
)

// Step 2: Entity owner approves
IPOnchain::foreign_authority_request_approve(
    origin,
    entity_id: 456,
    foreign_request_id: 1
)

// Step 3: Requester executes transfer
IPOnchain::foreign_authority_request_take(
    origin,
    foreign_request_id: 1,
    src: Location {                      // Parachain A location (for return reference)
        parents: 1,
        interior: [Parachain(1000)].into()
    }
)

On Parachain B (Destination):

The XCM message automatically calls access_wrapped_entity, which:

  • Creates a new entity with a new ID (e.g., 789)
  • Associates it with foreign authority 123
  • Stores a pointer: EntitiesPointer[789] = (Location(Parachain(1000)), 456)

Configuration Requirements

Pallet Config

impl pallet_ip_onchain::Config for Runtime {
    type ForeignRequestId = u32;
    type ForeignAuthorityId = u32;
    type ForeignMover = ForeignMoverImpl;
    // ... other config types
}

XCM Configuration

Ensure your runtime has:

  • pallet_xcm configured with appropriate barriers
  • XCM executor with AllowExplicitUnpaidExecutionFrom<Everything> or similar
  • XcmpQueue for message routing
  • Proper LocationToAccountId converters