It is now quite clear that the initial concern raised by the community about Celer’s upgradability contract, which we addressed here and plan to deprecation soon, is absolutely not bigger concerns comparing to many of the issues revealed and discussed in the forum.
While we have a lot to say about the ongoing debates in the forum and CT, we refrain from doing so and instead now deliver a multi-bridge Uniswap governance solution that is vendor-lock-in-free for Uniswap community to consider.
For Uniswap community who has not casted votes yet, please vote for Celer to express your support for this kind of multi-bridge solution.
This solution is strictly better than choosing a single bridge in terms of security and availability. In addition, it also retains the strongest bargaining power in the hands of Uniswap community to always receive the best services. See this for more context.
The implementation combines both Celer and Wormhole and is open-sourced here
All other bridges/cross-chain solutions such as LayerZero can be added easily via adapters.
We are setting up a testnet transaction to demonstrate this end-to-end. @Wormhole team we hope that you can help to set up a relayer for this because otherwise the only easy way would be deploying on Avalanche and BSC testnet where Wormhole generic relayers are available to our knowledge. Feel free to DM!
Now let’s get to the solution specification.
This is a solution for cross-chain message passing without vendor lock-in and with enhanced security beyond any single bridge. A message with multiple copies are sent through different bridges to the destination chains, and will only be executed at the destination chain when the same message has been delivered by a quorum of different bridges.
The current solution are designed for messages being sent from one source chain to multiple destination chains. It also requires that there is only one permitted sender on the source chain. This would be the use case for Uniswap governance contract on Ethereum
calling remote functions of contracts on other EVM chains.
To send a message to execute a remote call on the destintion chain, sender on the source chain should call
MultiBridgeSender, which invokes
sendMessage() of every bridge sender apdater to send messages via different message bridges.
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Source chain │ │ │ │ ┌─────────────────┐ ┌───────────────────┐ │ │ ┌──►│ Bridge1 Adapter ├──►│ Bridge1 Contracts │ │ │ │ └─────────────────┘ └───────────────────┘ │ │ │ │ │ ┌────────┐remoteCall()┌───────────────────┐sendMessage()│ ┌─────────────────┐ ┌───────────────────┐ │ │ │ Caller ├───────────►│ MultiBridgeSender ├─────────────┼──►│ Bridge2 Adapter ├──►│ Bridge2 Contracts │ │ │ └────────┘ └───────────────────┘ │ └─────────────────┘ └───────────────────┘ │ │ │ │ │ │ ┌─────────────────┐ ┌───────────────────┐ │ │ └──►│ Bridge3 Adapter ├──►│ Bridge3 Contracts │ │ │ └─────────────────┘ └───────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────┘
On the destination chain, MultiBridgeReceiver receives messages from every bridge receiver adapter. Each receiver adapter gets encoded message data from its bridge contracts, and then decode the message and call
MultiBridgeReceiver maintains a map from bridge adapter address to its power. Only adapter with non-zero power has access to
receiveMessage() function. If the accumulated power of a message has reached the a threshold, which means enough number of different bridges have delivered a same message, the message will be executed by the
The message execution will invoke a function call according to the message content, which will either call functions of other contracts, or call the param adjustment functions of the
MultiBridgeReceiver itself. Note that the only legit message sender is the trusted dApp contract on the source chain, which means only that single dApp contract has the ability to execute functions calls through the
MultiBridgeReceiver contracts on different other chains.
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Destination chain │ │ │ │ ┌───────────────────┐ ┌─────────────────┐ │ │ │ Bridge1 Contracts ├──►│ Bridge1 Adapter ├──┐ │ │ └───────────────────┘ └─────────────────┘ │ │ │ │ │ │ ┌───────────────────┐ ┌─────────────────┐ │receiveMessage()┌─────────────────────┐ call() ┌──────────┐ │ │ │ Bridge1 Contracts ├──►│ Bridge2 Adapter ├──┼───────────────►│ MultiBridgeReceiver ├────────►│ Receiver │ │ │ └───────────────────┘ └─────────────────┘ │ └─────────────────────┘ └──────────┘ │ │ │ │ │ ┌───────────────────┐ ┌─────────────────┐ │ │ │ │ Bridge2 Contracts ├──►│ Bridge3 Adapter ├──┘ │ │ └───────────────────┘ └─────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Below are steps to add a new bridge (e.g. Bridge4) by the dApp community.
- Bridge4 provider should implement and deploy Bridge4 adapters on source chain and all destination chains. The adapter contracts should meet the following requirements.
- On the source chain, the sender adapter should only accept
- On the destination chain, the receiver adapter should only accept messages sent from the Bridge4 sender adapter on the source chain, and then call
MultiBridgeReceiverfor each valid message.
- Renounce any ownership or special roles of the adapter contracts after inital parameter setup.
- On the source chain, the sender adapter should only accept
- Bridge4 provider deploy and open source the adapter contracts. dApp community should review the code and check if the requirements above are met.
- dApp contract (
Caller) on the source chain adds the new Bridge4 sender adapter to
MultiBridgeSenderon the source chain by calling the
- dApp contract (
Caller) on the source chain adds the new Bridge4 receiver adapter to
MultiBridgeReceiveron the destination chain by calling the
MultiBridgeSender, with arguments to call
MultiBridgeReceiveron the destination chain.
Updating quorum threshold is similar to configure a new bridge receiver adapter on destination chain. It requires a
remoteCall() from the source chain
Caller with calldata calling
updateQuorumThreshold() of the
MultiBridgeReceiver on the destination chain.
Use case: contract A on Goerli send message to contract B on BSC Testnet in order to call
enableFeeAmount() for state change. Apply a 2-of-3 messages governance model with message bridge C, D and E.
MultiBridgeSenderon Goerli, set address of A as allowed caller.
MultiBridgeReceiveron BSC Testnet.
- Each message bridge provider prepare their own
ReceiverAdapter, named with a prefix of their bridge name. Take preparation of
CReceiverAdapteras an example.
CSenderAdapteron Goerli, set address of
CReceiverAdapteron BSC Testnet, set address of
- Call updateReceiverAdapter() of
CSenderAdapter, set address of
CReceiverAdapteron BSC Testnet(chain id 97) as a valid ReceiverAdapter.
- Call updateSenderAdapter() of
CReceiverAdapter, set address of
CSenderAdapteron Goerli(chain id 5) as a valid SenderAdapter.
- Transfer ownership of
- Once all message bridges are ready, somehow let contract A call addSenderAdapters() of
MultiBridgeSenderwith an address array of
MultiBridgeReceiver, with an address array of
EReceiverAdapter, and power threshold 2.
Prepare a calldata for contract B for calling
enableFeeAmount(), then somehow let contract A call
_dstChainId = 97,
_target = <address of contract B> and
_callData = <calldata you prepared>.
Imagine that the messages sent via C, D and E received by
MultiBridgeReceiver on BSC Testnet in an order of
1.C 2.D 3.E. During receiving message sent via D, accumulated power reaches power threshold 2, which result in message execution(the calldata will be sent to contract B).
We are more than happy to answer any questions, help with tests, collaborate with similar-minded builders like @AlexSmirnov, iterate based on feedbacks and provide audits from community selected firms.
Although Celer, being a 2018-vintage project, does not have the backing of large UNI-holding investors at this moment, we do trust the integrity and intelligence of all the Uniswap delegates to choose this positive-sum path that ensures the highest level of security and service quality for Uniswap’s multi-blockchain expansion.
We will update this post with testnet transactions once we worked through the small integration work using @Wormhole .