Queries

XCM contains query instructions that can be used to query information from another consensus system:

  • ReportHolding
  • QueryPallet
  • ReportError
  • ReportTransactStatus

Each of these instructions is sent to the destination where we would like the information to be reported back to us. Each instruction has a QueryResponseInfo struct as one of its inputs.

pub struct QueryResponseInfo {
	pub destination: MultiLocation,
	#[codec(compact)]
	pub query_id: QueryId,
	pub max_weight: Weight,
}

The destination tells the queried consensus system where to send the response to and the query_id field links the query and the query response together. The max_weight field tells the queried consensus system what the maximum weight is that the response instruction can take.

When a query instruction is executed correctly, it sends a QueryResponse instruction to the location defined in the previously described destination field. The QueryResponse looks like this:

QueryResponse {
    #[codec(compact)]
    query_id: QueryId,
    response: Response,
    max_weight: Weight,
    querier: Option<MultiLocation>,
}

// Reponse Struct
pub enum Response {
	/// No response. Serves as a neutral default.
	Null,
	/// Some assets.
	Assets(MultiAssets),
	/// The outcome of an XCM instruction.
	ExecutionResult(Option<(u32, Error)>),
	/// An XCM version.
	Version(super::Version),
	/// The index, instance name, pallet name and version of some pallets.
	PalletsInfo(BoundedVec<PalletInfo, MaxPalletsInfo>),
	/// The status of a dispatch attempt using `Transact`.
	DispatchResult(MaybeErrorCode),
}

The QueryResponse has the same query_id as the request to link the request and response and takes over the max_weight from the QueryResponseInfo. It has the requested information in the response field. And it has the location of the querier relative to the queried location in the querier field. The response can be sent back to the requester, or to another location, so the querier field is important to determine where the requested information is needed.

Now we take a look at the query instructions.

ReportHolding

ReportHolding { response_info: QueryResponseInfo, assets: MultiAssetFilter }

The ReportHolding instruction reports to the given destination the contents of the Holding Register. The assets field is a filter for the assets that should be reported back. The assets reported back will be, asset-wise, the lesser of this value and the holding register. For example, if the holding register contains 10 units of some fungible asset and the assets field specifies 15 units of the same asset, the result will return 10 units of that asset. Wild cards can be used to describe which assets in the holding register to report, but the response always contains assets and no wild cards.

Example

For the full example, check here. Assets are withdrawn from the account of parachain 1 on the relay chain and partly deposited in the account of parachain 2. The remaining assets are reported back to parachain 1.

Xcm(vec![
    WithdrawAsset((Here, AMOUNT).into()),
    BuyExecution { fees: (Here, AMOUNT).into(), weight_limit: Unlimited },
    DepositAsset { assets: Definite((Here, AMOUNT - 5).into()), beneficiary: Parachain(2).into() },
    ReportHolding {
        response_info: QueryResponseInfo {
            destination: Parachain(1).into(),
            query_id: QUERY_ID,
            max_weight: Weight::from_all(0),
        },
        assets: All.into(),
    },
]);

QueryPallet

The QueryPallet instruction queries the existence of a particular pallet based on the module name specified in the module_name field.

QueryPallet { module_name: Vec<u8>, response_info: QueryResponseInfo }

The destination responds with a vec of PalletInfos if the pallet exists.

pub struct PalletInfo {
	#[codec(compact)]
	index: u32,
	name: BoundedVec<u8, MaxPalletNameLen>,
	module_name: BoundedVec<u8, MaxPalletNameLen>,
	#[codec(compact)]
	major: u32,
	#[codec(compact)]
	minor: u32,
	#[codec(compact)]
	patch: u32,
}

Example

For the full example, check here. It queries for all instances of pallet_balances and sends the result back to parachain 1.

Xcm(vec![
    QueryPallet {
        module_name: "pallet_balances".into(),
        response_info: QueryResponseInfo {
            destination: Parachain(1).into(),
            query_id: QUERY_ID,
            max_weight: Weight::from_all(0),
        },
    }
]);

ReportError

The ReportError instruction report the contents of the Error Register to the given destination. This instruction is useful in combination with the SetErrorHandler instruction. It then only reports an error if an error is thrown.

ReportError(QueryResponseInfo)

Example

For the full example, check here. The message sets the error handler to report back any error that is thrown during execution of the instructions using the ReportError instruction.

Xcm(vec![
    // Set the Error Handler to report back status of Error register.
    SetErrorHandler(Xcm(vec![
        ReportError(QueryResponseInfo {
            destination: Parachain(1).into(),
            query_id: QUERY_ID,
            max_weight: Weight::from_all(0),
        })
    ])),
    // If an instruction errors during further processing, the resulting error is reported back to Parachain(1).
    // MORE INSTRUCTIONS
]);

ReportTransactStatus

The ReportTransactStatus instruction report the value of the Transact Status Register to the specified destination.

ReportTransactStatus(QueryResponseInfo)

Example

For the full example, check here. Dispatches a call on the consensus system receiving this Xcm and reports back the status of the Transact Status Register.

Xcm(vec![
    Transact {
        origin_kind: OriginKind::SovereignAccount,
        require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024),
        call: remark.encode().into(),
    },
    ReportTransactStatus(QueryResponseInfo {
        destination: Parachain(1).into(),
        query_id: QUERY_ID,
        max_weight: Weight::from_all(0),
    }),
]);