Try   HackMD

XCM support in Smart Contracts

TODO: Add scenarios?

I'm diving this into three parts: Tokens, Queries and Fees.

Tokens

In order to operate with ERC20 tokens, a user needs to call specific functions in its origin smart contract, for example, for transferring it.
In XCM, assets are operated upon by means of the TransactAsset trait.
In order for XCM to recognize we are dealing with an ERC20, we would need to create an asset transactor to handle them.

This asset transactor has to match ERC20s based on their id. A possible one is the following: [PalletInstance(<pallet-revive index>), AccountKey20 { id }] where id is the contract's address.
Then, different XCM instructions need to call functions in the contract, like TransferAsset, DepositAsset and WithdrawAsset.

TransferAsset would call transfer.
With WithdrawAsset and DepositAsset we could call something like burn and mint respectively or we could transfer to a checking account.

Acala has one for this purpose which we could use as a reference.

We probably need the same for ERC721 but I'm less familiar with that standard.
We could make use of the traits for NFTs.

Queries

This could be further split into receiving and sending.

Sending

In order to send a query we need a host function for contracts that calls pallet-xcm's new_query function to store a Query.

Scenarios

Both scenarios assume that a new query is created using the XCM precompile. Then, an XCM message (e.g.: Xcm(vec![Transact {...}, ReportTransactStatus(QueryResponseInfo {queryId …}])) should be sent to the destination chain, essentially linking a queryId to some bytes that are meant to be executed.

Perform a remote call and get the result of the execution

We need to have a way of getting the result of the execution of a remote call. This should return a response with the result. In case of failing, the response should contain the actual error.

Query external data / Call view functions

Allow the smart contracts on Asset Hub to query data on external consensus systems. This might be a call to a specific pallet or a view function in a smart contract. We should be able to send PVQ blobs using the new ReportQuery instruction.

Receiving

A message is received by the runtime to respond to a query with some query id.
This will match the query id of the query created previously.

We need a host function in contracts that calls pallet-xcm's take_response to poll for the response, this could result in an callback, which could be handled by a function in the contract.

Fees

Pallet-revive charges an additional fee for storage deposits.
We can use the new fees register to charge this fee.
We need some place to charge them, be it in CallDispatcher for Transact or in the new instruction EvmCall if we decide to go for it.

The EvmCall instruction would be easier since we already know we have to deal with these storage deposits.

The fees register can also be exposed to the transactor to charge storage deposits.

Storage deposits need to be charged in this way, but regular gas cost could be charged up-front with PayFees, as part of execution costs.
An open question I have is if we should separate them or treat them in the same way.

We would need to make sure the DryRunApi can successfully dry-run a program with EVM calls.
We already have calls for execution fees and delivery fees in XcmPaymentApi. Do we just add another one for EVM storage deposits?

Should we just have a more powerful DryRunApi that returns all fees: execution, delivery, EVM storage deposits, etc.