-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
L2 direct bridging #792
L2 direct bridging #792
Conversation
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8081515935 check. |
9583e66
to
bafffde
Compare
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8081877407 check. |
bafffde
to
dbeb072
Compare
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8082593661 check. |
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.
dbeb072
to
970fed2
Compare
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8083002051 check. |
Here we present a draft implementation of the `L1BitcoinDepositor` contract that acts as the central point of the tBTC direct bridging feature on the L1 Ethereum chain where TBTC minting occurs. This contract exposes the `receiveWormholeMessages` function that receives a message from `L2BitcoinDepositor` contract (using the Wormhole Relayer infrastructure), extracts deposit data from it, and initiates the deposit on the tBTC `Bridge` side. Moreover, the contract also exposes the `initializeDeposit` function directly. The goal here is to satisfy future use cases that don't rely on Wormhole Relayer for cross-chain messaging.
So far, the `_initializeDeposit` accepted `calldata` parameters. This does not work for integrator contracts that obtain deposit parameters as `memory` objects (the conversion `memory -> calldata` is not possible). To overcome this problem, we are changing parameters of `_initializeDeposit` to be `memory` as well. This is not breaking integrators that receive deposit parameters as `calldata` because the `calldata -> memory` conversion is fine.
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8083278660 check. |
This will be useful to get TBTC token address directly from the vault contract.
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8097115790 check. |
Here we implement the finalization of the deposit flow. This process is started on the `L1BitcoinDepositor` side once the tBTC `Bridge` completes minting of the TBTC token. The process consists of multiple steps: 1. The `L1BitcoinDepositor` marks the given deposit as finalized and determines the amount of TBTC minted. 2. The `L1BitcoinDepositor` initiates a Wormhole token transfer. This transfer locks minted TBTC on L1 within the Wormhole Token Bridge and unlocks Wormhole-wrapped L2 TBTC for the `L2WormholeGateway` contract. 3. The `L2BitcoinDepositor` sends the transfer VAA to `L2BitcoinDepositor`. 4. The `L2BitcoinDepositor` finalizes the transfer by calling the `L2WormholeGateway` that redeems Wormhole-wrapped L2 TBTC from the Wormhole Token Bridge and uses it to mint canonical L2 TBTC for the L2 deposit owner.
ef54871
to
5a179fd
Compare
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8097176915 check. |
Here we adjust the new contracts for deployment: - We are introducing the `attach*BitcoinDepositor` functions to solve the chicken & egg problem that occurs upon deployment - We are adjusting gas limits for Wormhole to real-world values - We are getting rid of cross-chain Wormhole refunds that don't work for small amounts
Here we add deployment scripts for both `L*BitcoinDepositor` contracts. We use Base as the reference chain.
We are testing the implementation by deploying it on Base Sepolia chain. Here are the relevant deployment artifacts.
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8110832044 check. |
Such a deposit reveal was tested for Base -> Ethereum and turned out to be super expensive. It takes ~0,045 ETH so ~150 USD to transfer deposit data to Ethereum. This is an unacceptable cost. To overcome that problem, we are abandoning the idea of using Wormhole Relay for that in favor of our own relay bot implementation. This also makes sense as eventually, we are aiming towards using gas-less reveals on Base.
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8139957444 check. |
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8143372301 check. |
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8153045964 check. |
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8155881875 check. |
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8173029249 check. |
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8185130042 check. |
The OZ upgrade plugin complains about unsafe upgrade if standard versions of `IERC20` and `SafeERC20` are used.
We need to make sure only owner can call this function.
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8185638599 check. |
Solidity API documentation preview available in the artifacts of the https://github.com/keep-network/tbtc-v2/actions/runs/8185665844 check. |
It's enough to store the `gasSpent` as `uint96` which allows us to save one storage slot. Moreover, we are increasing the default value of `initializeDepositGasOffset` to adhere to real-world gas consumption.
function initializeV2(string memory _newVar) public { | ||
newVar = _newVar; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please drop an empty line at each of the files?
Closes: #750 This pull request enhances the tBTC Typescript SDK with support of the L2 direct bridging mechanism introduced by #792. Moreover, we are adding Base as the first L2 chain with direct bridging enabled. ### Initialization of the cross-chain contracts To enable the SDK to work with a supported L2 chain, we introduce a new `initializeCrossChain` method that is exposed from the main `TBTC` component. This function sets up the SDK to work with the given L2 chain and attaches the provided signer to the tBTC cross-chain contracts deployed there. It's worth noting that the signer can be read-only (e.g. Ethers provider) but in that case, only read functions of the L2 contracts will be available. After initialization, the cross-chain contracts for the given chain can be directly accessed using the new `TBTC.crossChainContracts` method. This low-level access method is especially useful for reading data, e.g. get canonical L2 TBTC balance for the given address. ### Cross-chain deposits To leverage the new L2 direct bridging mechanism, the SDK adds the concept of a cross-chain deposit. A cross-chain deposit is a deposit that targets an L2 chain other than the L1 chain the tBTC system is deployed on. Such a deposit is currently initiated using a transaction on the L2 chain (plans for the future include gas-less initiation). On the technical level, this is handled by the new `initiateCrossChainDeposit` method exposed by the `DepositsService` component. ### Usage ```typescript import { Hex, TBTC } from "../src" import { JsonRpcProvider, Provider } from "@ethersproject/providers" import { Signer, Wallet } from "ethers" async function main() { // Create a readonly Ethers provider for the Ethereum L1 chain. const ethereumProvider: Provider = new JsonRpcProvider("...") // Create an instance of the tBTC SDK. It is enough to pass a readonly // Ethers provider as parameter. In this example, the SDK does not issue // transactions on the Ethereum L1 chain. const sdk: TBTC = await TBTC.initializeMainnet(ethereumProvider) // Create a signer for Base. This signer will be used to issue transactions // on the Base chain and will be used as the owner of the deposit. const baseSigner: Signer = new Wallet("...", new JsonRpcProvider("...")) // Initialize cross-chain contracts for the Base chain. await sdk.initializeCrossChain("Base", baseSigner) // Get BTC recovery address for the deposit. const bitcoinRecoveryAddress: string = "..." // Initiate a cross-chain deposit to the Base chain. const deposit = await sdk.deposits.initiateCrossChainDeposit( bitcoinRecoveryAddress, "Base" ) // Get BTC deposit address and send funds to it. const depositAddress: string = await deposit.getBitcoinAddress() // Initiate minting once BTC transaction is made. This will call // revealDepositOnBehalf function of the ExampleDepositor contract // under the hood. const baseTxHash: Hex = await deposit.initiateMinting() console.log( `Minting initiated. Base transaction hash: ${baseTxHash.toPrefixedString()}` ) } ```
Who will be running the off-chain relayer bot? Will it be a single centralized entity? |
Refs: #750
Here we present the smart contracts necessary to enable the L2 direct bridging (a.k.a native bridging) feature. This mechanism allows getting canonical TBTC on the given supported L2 chain, without the need to touch the L1 Ethereum chain the tBTC protocol is deployed on.
Changes made as part of this pull request introduce a generic mechanism that can be deployed on all supported L2 EVM-based chains and deploy the mechanism on Ethereum Sepolia and Base Sepolia chains for testing purposes.
Motivation
Right now, a user of the supported L2 chain willing to obtain canonical L2 TBTC has to go the following path:
This flow is unwieldy and has major drawbacks:
The idea behind direct bridging is simplifying the above flow to something like:
Although this flow still relies on Wormhole underneath, the advantages are:
High-level architecture
The high-level architecture of the direct briding mechanism is presented on the chart below:
TBTC Bridge
component is theBridge
contract deployed on L1 Ethereum responsible for minting the L1 TBTC token. TheL2WormholeGateway
contract has the authority to mint canonical L2 TBTC on the given L2, based on received Wormhole L2 TBTC tokens. TheL2TBTC
component is the canonical L2 TBTC token contract.AbstractTBTCDepositor
contract (introduced by TheAbstractTBTCDepositor
contract #778) provides some useful tooling facilitating integration with the tBTCBridge
and its new deposit with extra data function (developed in ExposerevealDepositWithExtraData
in theBridge
contract #760) which is the foundation of the L2 direct bridging mechanism. TheL1BitcoinDepositor
andL2BitcoinDepositor
components are smart contracts handling the actual direct bridging actions on the L1 and L2 chains respectively. Those two contracts are introduced by this pull request.TokenBridge
contract handles the bookkeeping part of the transfer. TheRelayer
contract handles the actual execution of it.Bridge
contract.The above components interact with each other in the following way:
L2BitcoinDepositor.initializeDeposit
function to initiate the deposit process on L2 (the call is made through a dApp and tBTC SDK).DepositInitialized
event emitted by theL2BitcoinDepositor
contract.L1BitcoinDepositor.initializeDeposit
function on L1.L1BitcoinDepositor
contract calls theBridge.revealDepositWithExtra
function under the hood (through theAbstractTBTCDepositor
abstract contract).Bridge
contract mints L1 TBTC to theL1BitcoinDepositor
contract.L1BitcoinDepositor.finalizeDeposit
to start the finalization process and move L1 TBTC to the L2 chain.L1BitcoinDepositor
calls Wormhole'sTokenBridge.transferTokensWithPayload
function to initiate the cross-chain transfer of L1 TBTC. This call pulls out the L1 TBTC from theL1BitcoinDepositor
contract and locks it in theTokenBridge
.L1BitcoinDepositor
calls Wormhole'sRelay.sendVaasToEvm
to send a cross-chain message toL2BitcoinDepositor
and notify it about a pending cross-chain transfer.L2BitcoinDepositor.receiveWormholeMessages
function to deliver the cross-chain message.L2BitcoinDepositor
contract calls theL2WormholeGateway.receiveTbtc
function under the hood. It passes the VAA representing the cross-chain transfer as an argument of the call.L2WormholeGateway
uses the obtained VAA to finalize the cross-chain transfer by calling the Wormhole'sTokenBridge.completeTransferWithPayload
function. This call redeems Wormhole-wrapped L2 TBTC from theTokenBridge
.L2WormholeGateway
uses obtained Wormhole-wrapped L2 TBTC to callL2TBTC.mint
and mint canonical L2 TBTC.Immediate next steps
Changes presented in this pull request introduce the on-chain components of the direct bridging mechanism. To make the mechanism complete, the following steps need to take place:
Next iteration: Gasless bridging
The plans for the future include some improvements to the first iteration of the direct bridging mechanism described above. Namely, the next iteration will bring gasless direct bridging that will not require any L2 transaction to initiate the process and will rely on a single Bitcoin funding transaction issued by the L2 user. On the technical level, the first two steps of the flow will be replaced by a direct call to the off-chain relayer's REST endpoint: