Allow bridge and FRAME applications to live on different parachains
The goal is to allow the bridge module to interact with ETH and ERC20 applications living on separate parachains. Messaging will be implemented using XCMP.
The interaction should be bidirectional:
- Minting eth: Bridge sends message to ETH App
- Burning eth: EthApp sends message to Bridge
We'll need to come up with a design for this, and think carefully about issues of security and trust.
Some of the issues we'll need to implement to make this possible:
- [ ] Add public dispatchables to bridge and FRAME applications
- [ ] Allow dynamic registration of FRAME applications with the bridge
Following a call the Efinity team had with @musnit and @vgeddes, @fmiguelgarcia and I have considered how to implement message forwarding to snowbridge and resolve this issue.
Message Forwarding
Message forwarding is leveraging XCM so that snowbridge is able to forward calls emitted by contracts to a the 3rd-party parachain that is interested in handling these calls.
To enable message forwarding on Snowbridge, one of the following 3 approaches has to be implemented:
- ERC20 → Parachain Mapping (1 to 1)
- Parachain specific contracts
- Parachain ID as parameter
Solution 1: ERC20 → Parachain Mapping
In this solution, a pallet is added to snowbridge where the ERC20 token is registered along with the parachain ID that handles this ERC20 token. This means that calls dispatched into the bridge will need to be routed to the designated parachain depending on this mapping.
Pros:
- Easy to implement
Cons:
- Mapping restricts multiple apps from leveraging the bridge to operate the token. Example: multiple parachain who want to operate on USDT.
- Implicit Mapping, meaning that the user has to rely on the fact that the mapping that the bridge has will lead to the token landing on the correct parachain.
Solution 2: Parachain specific contracts
In this solution:
- Each parachain has it’s own ERC20App, ERC721, NFT, DOTApp contracts deployed.
- These contracts need to include their own addresses in the events emitted.
- Relayer submits the call to the bridge parachain along with the originating address of the read event.
- A pallet is introduced in the bridge parachain to map these contract addresses to the parachain that handles events emitted from these contract.
Pros:
- Isolate parachain specific contracts from other parachains.
- Supports the use case for multiple parachains being able to handle the same token.
Cons:
- Contracts deployment is left to the parachain maintainers while not leveraging contracts deployed by the bridge itself.
- Implicit Mapping, meaning that the user has to rely on the fact that the mapping that the bridge has will lead to the token landing on the correct parachain.
- Relayer has to support dynamic registration of contracts bundle for each parachain to watch events coming out.
Solution 3: Parachain ID as parameter
In this solution:
- ERC20App, ERC721App, ETHApp contracts should be modified to accept Parachain ID to which the transfer will be relayed. A Parachain ID of 0 could mean the transfer is meant for the bridge itself.
- The relayer detects events and submits messages from/to Inbound / Outbound channels.
- For Ethereum, processing inbound messages means submitting transactions to the said contracts.
- For Snowbridge’s parachain, the messages are registered into the Inbound channel and those messages are dispatched by the dispatcher pallet into the final targeted pallet to process the call.
- Only in Eth → Parachain case, the bridge extracts the parachain ID from the call’s payload and sends an XCM message to the targeted parachain.
Pros:
- Explicit target parachain
- No mapping required
- No additional contract deployments required
Cons:
- User has to be aware of the parachain ID when an eth transaction to lock is submitted.
We are leaning towards solution 3 since it:
- Requires minimal modifications
- Leverages the most out of existing infrastructure that is going to be offered by snowbridge.
Please let us know your thoughts on the above so that we can collaborate on the implementation.
This is great thanks!
We're definitely in agreement for Option 3 and that's our next most important step to make!
In addition, we're also speccing out something like Option 2 as well to allow for parachains to deploy custom functionality and their own contracts and allow for custom cross-chain smart contract calls. The main difference to your design in Option 2 is that we're likely to push the parachain<>smart contract address mapping up to the application level rather than having it as part a pallet in the core bridge. (ie, similar to how in Option 3 the smart contract controls the parachain ID. Similarily, parachains will be able to specify a destination smart contract in their XCM messages for the reverse direction.)
Sounds great. I'll start putting together a PR with changes towards Option3.
I've discussed this topic some more with Aidan. To clarify, a variant of Option 3 is really only meaningful for our own bridged assets (SnowETH for example). Where initiated by a single transaction (aka "1-click"), users can transfer Ether from Ethereum to our parachain, and then onto another parachain (Moonbeam, Acala, etc).
for Efinity's use-case, if you do need more flexibility, we may need to go for something similar to option 2.
As Aidan mentioned, I've been fleshing out a design for it at https://snowfork.notion.site/Low-level-channels-API-18b49af93ab741e28f49843a0eea3ace.
As with your idea for solution 2, it will require that you build a pallet and smart contract for the asset you want to transfer/teleport. There would be no need for any changes to our relayer services, so there may have been a misunderstanding there. By design our relayers only watch channels, and has no knowledge of the arbitrary apps that submit messages to channels.
I'll be kicking off work on the low-level channels API this week. Not sure we need any development help initially, as I'll will be focusing on refactoring our core APIs and protocols. But at some point, we need to test the new APIs, and then we can collaboratively work on an integration.
Anyway, if you have thoughts or suggestions about the design doc, let me know.
@vgeddes I like the approach for low-level channel APIs.
Looking at existing snowbridge contracts / relayer code, the relayers calls into the parachain using Utility.batch_all. Is there a need to change this from sending a list encoded Calls to just regular struct based messages in order for the snowbridge parachain to decide to dispatch locally vs dispatch to a 3rd-party parachain? i am thinking that with the current approach, the way Calls are encoded requires that we change the (ETH | ERC20 | ERC721)Apps to accept the destination parachain ID and that's where the XCM would be sent. However, that IMO would be wrong since the dispatch pallet needs to do this.
What do you think?
@rakanalh i think we may want both.
for a true low level call, as vincent's been looking into, the dispatch pallet or some new pallet will send the xcm. for ETH | ERC20 | ERC721 as in Option 3, the ETH | ERC20 | ERC721 will send the xcm
but yes still need to look into the best place to put the parachain ID to support both cases - likely will be some extra standard we add to our existing messages.
@vgeddes I like the approach for low-level channel APIs.
Cool 👍
Looking at existing snowbridge contracts / relayer code, the relayers calls into the parachain using
Utility.batch_all. Is there a need to change this from sending a list encodedCalls to just regular struct based messages in order for the snowbridge parachain to decide to dispatch locally vs dispatch to a 3rd-party parachain?
@rakanalh The relayer is already sending regular struct based messages to the parachain. These messages are Ethereum event logs which include a lot of information besides the encoded Call (the message payload). See https://github.com/Snowfork/snowbridge/blob/aaee56cf85e38ff5a91bd5cac9807814d85f1fbd/ethereum/contracts/IncentivizedOutboundChannel.sol#L23 https://github.com/Snowfork/snowbridge/blob/aaee56cf85e38ff5a91bd5cac9807814d85f1fbd/parachain/pallets/incentivized-channel/src/inbound/envelope.rs#L24
I anticipate we'll just need to update the message struct to include a paraId field. Then as Aidan said, some pallet will either dispatch locally or forward onto another parachain.
@rakanalh Work on our low-level channels API is progressing well, with most of the ethereum -> polkadot flow implemented. I'm expecting to release the feature for beta testing in early to mid December.
Working branch: https://github.com/Snowfork/snowbridge/compare/vincent/channels-api
I'll be cutting a draft PR soon for some interim design/implementation review.