Moonbeam wants to support relay chain token transfers on its own chain. In order to do so, it will lock up DOTs in the parachain sovereign account and mint VDOTS (1:1) to a Moonbeam address (specified by the sender of the DOTs to the parachain sovereign account). The Moonbeam account can burn the VDOTs to transfer DOTs back out of the parachain sovereign account (1:1) into a specified relay chain account, but this becomes complicated when the Moonbeam account that is burning VDOT is a smart contract because smart contracts do not have private keys and cannot store relay chain private keys to control relay chain accounts.

Here is the example scenario in question:

  1. DOTs are sent to the Moonbeam sovereign account (only relay chain account controlled by the parachain) with one additional argument, a Moonbeam address which will receive VDOT (a 1:1 derivative, transferrable on Moonbeam)
  2. As soon as (1) happens, a downward message is sent to the parachain to mint VDOTs to the user-specified Moonbeam account. Moonbeam accounts can be users or smart contract addresses.
  3. If the account receiving VDOT in (2) is a smart contract, then burning the VDOT to mint DOT on the relay chain becomes nontrivial. The smart contract will need a derived relay chain account that is only controllable by the smart contract in order to receive DOT if the smart contract burns the VDOT.
  4. To derive a relay chain account (without a private key) for the smart contract to control, the XCM::InitiateReserveWithdraw message in (3) needs to have a side effect which is derive a proxy account from the parachain sovereign address such that this relay chain account is only controllable by the smart contract. The proxy account derivation happens on the relay chain so the parachain needs to read the associated events to know the generated proxy account and store it locally as the address controlled by the smart contract’s origin.

(4) would not require the event emission associated with proxy account derivation if the relay chain could communicate (i) the relay chain height && (ii) extrinsic index associated with the proxy account derivation to the parachain. With this data, account derivation is deterministic.

Requirement 1: Upward Message Success/Failure Response to Parachain + Events That Executed

The parachain needs the relay chain to notify the parachain if an Upward Message executes successfully on the relay chain.

The current hacky way of getting this result is to append the message Order::QueryHolding(id) to all upward messages. If the Xcm::QueryResponse(id) (with same id as QueryHolding) is received by the parachain as a downward message, then this is interpreted as successful relay chain execution by the parachain.

We could implement a N block timeout such that after N blocks without the receipt message, the previously sent upward message failed during execution. If an upward message execution failure is interpreted by the parachain, all local effects executed prior to sending the upward message must be reverted (reversion logic not yet implemented).

In addition to knowing whether the upward message failed or succeeded, the parachain also needs all the events emitted on the relay chain as a result of the upward message execution/failure. In particular, this would enable Moonbeam to use derived proxy accounts to express account ownership for smart contracts (as described further above).

Requirement 2: Parachain needs to encode Transact(call) such that call corresponds to a relay chain call

There is no clear way to know if the call bytes passed into Xcm::Transact are encoded correctly because the parachain doesn’t have the context to verify correct relay chain encoding. Even if runtime integration tests are written to verify that some version of Polkadot has calls that correspond to the encoded bytes, the relay chain can always upgrade, thereby breaking the encoded call format.

It may be useful if there was an XCM sent from the relay chain to the parachain after a relay chain runtime upgrade. This XCM could turn off the parachain’s XCM-Wrapper (used to encode the relay chain calls) until it is reactivated by democracy/tech committee (after it has been verified that call encoding is compatible). If the relay chain call format changes, the XCM-Wrapper would be disabled until the parachain runtime upgrade to update the call format.

Proposal 1: Maintaining Local State for Proactive Error Handling

A common pattern is for the parachain to optimistically execute local effects before sending an XCM to another chain. If the XCM fails on the other chain, the local effects that were optimistically executed need to be reverted.

A simple example is transferring tokens from Parachain A to Parachain B. In order for the transfer to succeed, there must already exist an HRMP channel between the two parachains. In the current implementation, the tokens are burned on Parachain A before the XCM is sent, under the assumption that the same quantity of tokens will be minted on Parachain B after the XCM is executed. If an HRMP channel does not exist between the parachains, the XCM execution fails to mint on Parachain B, thereby requiring that the burn on Parachain A is reverted (reversion of local effects isn’t yet implemented).

A better design is to catch the error before executing local effects and before sending the XCM. We propose adding a pallet to all parachains which stores all the HRMP channels open between itself and other parachains. Before an XCM is sent from Parachain A to Parachain B, there is a check of local storage to see if an HRMP channel already exists. If the channel does not exist, the local effects (burning token) are not executed and the XCM is not sent.

The hrmp-channels pallet for storing and updating all open HRMP channels for each parachain was prototyped here.