From dbeb07225047491d8917b3a8c43cdfed2dd085e5 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Wed, 28 Feb 2024 14:48:22 +0100 Subject: [PATCH] Draft implementation of the `L2BitcoinDepositor` contract Here we present a draft implementation of the `L2BitcoinDepositor` contract that acts as an entrypoint of the tBTC direct bridging feature on the given L2 chain. This contract exposes the `initializeDeposit` function that takes the deposit data (funding tx, reveal info, original depositor) and relays it to the `L1BitcoinDepositor` contract using the Wormhole Relayer infrastructure. --- solidity/contracts/l2/L2BitcoinDepositor.sol | 139 +++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 solidity/contracts/l2/L2BitcoinDepositor.sol diff --git a/solidity/contracts/l2/L2BitcoinDepositor.sol b/solidity/contracts/l2/L2BitcoinDepositor.sol new file mode 100644 index 000000000..408d17bbe --- /dev/null +++ b/solidity/contracts/l2/L2BitcoinDepositor.sol @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-3.0-only + +// ██████████████ ▐████▌ ██████████████ +// ██████████████ ▐████▌ ██████████████ +// ▐████▌ ▐████▌ +// ▐████▌ ▐████▌ +// ██████████████ ▐████▌ ██████████████ +// ██████████████ ▐████▌ ██████████████ +// ▐████▌ ▐████▌ +// ▐████▌ ▐████▌ +// ▐████▌ ▐████▌ +// ▐████▌ ▐████▌ +// ▐████▌ ▐████▌ +// ▐████▌ ▐████▌ + +pragma solidity ^0.8.17; + +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +import "../integrator/IBridge.sol"; + +/// @title IWormholeRelayer +/// @notice Wormhole Relayer interface. Contains only selected functions +/// used by L2BitcoinDepositor. +/// @dev See: https://github.com/wormhole-foundation/wormhole-solidity-sdk/blob/2b7db51f99b49eda99b44f4a044e751cb0b2e8ea/src/interfaces/IWormholeRelayer.sol#L74 +interface IWormholeRelayer { + /// @dev See: https://github.com/wormhole-foundation/wormhole-solidity-sdk/blob/2b7db51f99b49eda99b44f4a044e751cb0b2e8ea/src/interfaces/IWormholeRelayer.sol#L122 + function sendPayloadToEvm( + uint16 targetChain, + address targetAddress, + bytes memory payload, + uint256 receiverValue, + uint256 gasLimit, + uint16 refundChain, + address refundAddress + ) external payable returns (uint64 sequence); + + /// @dev See: https://github.com/wormhole-foundation/wormhole-solidity-sdk/blob/2b7db51f99b49eda99b44f4a044e751cb0b2e8ea/src/interfaces/IWormholeRelayer.sol#L442 + function quoteEVMDeliveryPrice( + uint16 targetChain, + uint256 receiverValue, + uint256 gasLimit + ) + external + view + returns ( + uint256 nativePriceQuote, + uint256 targetChainRefundPerGasUnused + ); +} + +// TODO: Document this contract. +contract L2BitcoinDepositor is OwnableUpgradeable { + // TODO: Document state variables. + IWormholeRelayer public wormholeRelayer; + uint16 public l2ChainId; + uint16 public l1ChainId; + address public l1BitcoinDepositor; + uint256 public l1InitializeDepositGasLimit; + + event DepositInitialized( + bytes32 payloadHash, + uint64 sequence, + address indexed depositOwner, + address indexed sender + ); + + event L1InitializeDepositGasLimitUpdated(uint256 l1InitializeDepositGasLimit); + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize( + address _wormholeRelayer, + uint16 _l2ChainId, + uint16 _l1ChainId, + address _l1BitcoinDepositor + ) external initializer { + __Ownable_init(); + + wormholeRelayer = IWormholeRelayer(_wormholeRelayer); + l2ChainId = _l2ChainId; + l1ChainId = _l1ChainId; + l1BitcoinDepositor = _l1BitcoinDepositor; + l1InitializeDepositGasLimit = 200_000; + } + + // TODO: Document this function. + function updateL1InitializeDepositGasLimit(uint256 _l1InitializeDepositGasLimit) + external + onlyOwner + { + l1InitializeDepositGasLimit = _l1InitializeDepositGasLimit; + emit L1InitializeDepositGasLimitUpdated(_l1InitializeDepositGasLimit); + } + + // TODO: Document this function. + function initializeDeposit( + IBridgeTypes.BitcoinTxInfo calldata fundingTx, + IBridgeTypes.DepositRevealInfo calldata reveal, + address depositOwner + ) external payable { + // Cost of requesting a `initializeDeposit` message to be sent to + // `l1Chain` with a gasLimit of `l1InitializeDepositGasLimit`. + uint256 cost = quoteInitializeDeposit(); + + require(msg.value == cost, "Payment for Wormhole Relayer is too low"); + + bytes memory payload = abi.encode(fundingTx, reveal, depositOwner); + + uint64 sequence = wormholeRelayer.sendPayloadToEvm{value: cost}( + l1ChainId, + l1BitcoinDepositor, + payload, + 0, // No receiver value needed. + l1InitializeDepositGasLimit, + l2ChainId, // Set this L2 chain as the refund chain. + msg.sender // Set the caller as the refund receiver. + ); + + emit DepositInitialized( + keccak256(payload), + sequence, + depositOwner, + msg.sender + ); + } + + // TODO: Document this function. + function quoteInitializeDeposit() public view returns (uint256 cost) { + (cost, ) = wormholeRelayer.quoteEVMDeliveryPrice( + l1ChainId, + 0, // No receiver value needed. + l1InitializeDepositGasLimit + ); + } +}