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:
- ForeignMover Trait - Defines the interface for sending entities across chains
- Foreign Request System - Manages the workflow of cross-chain entity transfers
- 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:
- User initiates a request to transfer an entity to another parachain
- A new
Foreigntype authority is automatically created on the local chain - A foreign request is stored with
approval: falseanddone: false - Event
ForeignRequestsAddedis 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:
- Entity owner approves the transfer request
- Access control checks ensure the caller has
WrapEntitypermission - Foreign request is marked as
approval: true - Event
EntityWrapedis 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:
- Original requester executes the approved transfer
- Validation checks:
- Requester matches original
whoaccount - Request is approved (
approval == true) - Request not already completed (
done != true)
- Entity ownership is updated to the foreign authority
- XCM message is sent via
ForeignMover::send_wrapped() - Request is marked as
done: true - Events
EntityMovedForeignandForeignRequestsDoneare 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:
- Receives entity data from source parachain via XCM
- Converts foreign authority ID to local authority ID
- Creates a new entity with a new entity ID
- Stores pointer to original location and entity ID
- Event
EntityAddedis 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:
- WrapEntity Permission - Required to approve foreign requests
- Request Ownership - Only the original requester can execute the transfer
- Approval Status - Requests must be approved before execution
- 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_xcmconfigured with appropriate barriers- XCM executor with
AllowExplicitUnpaidExecutionFrom<Everything>or similar XcmpQueuefor message routing- Proper
LocationToAccountIdconverters