Skip to content

Commit

Permalink
L2 direct bridging (#792)
Browse files Browse the repository at this point in the history
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:
1. Generate a Bitcoin deposit address (using tBTC dApp or SDK)
2. Make a deposit transaction on Bitcoin
3. Reveal the Bitcoin transaction to the tBTC Bridge to start the
minting process (Ethereum transaction)
4. Once TBTC is minted on Ethereum, go to the [Wormhole Token
Portal](https://portalbridge.com) and bridge minted TBTC to the given L2
(another Ethereum transaction)
5. Canonical L2 TBTC lands on the user account automatically

This flow is unwieldy and has major drawbacks:
- It's complicated and requires multiple transactions
- It requires L2 users to interact with the L1 Ethereum chain
- It requires interacting with the Wormhole Token Portal

The idea behind direct bridging is simplifying the above flow to
something like:
1. Generate a Bitcoin deposit address (using dApp or SDK)
2. Make a deposit transaction on Bitcoin
3. Reveal the Bitcoin transaction to the tBTC Bridge **using a single
transaction on the L2 chain**
4. Canonical L2 TBTC lands on the user account automatically

Although this flow still relies on Wormhole underneath, the advantages
are:
- Simpler flow with fewer transactions
- L2 users interact only with the given L2 chain
- No need to touch the Wormhole Token Portal
- As a next iteration, we can even get rid of the reveal deposit
transaction on L2 and use Bitcoin deposit transactions as a trigger.
This will improve UX even more. See the **Next iteration: Gasless
bridging** section for details.

### High-level architecture

The high-level architecture of the direct briding mechanism is presented
on the chart below:

<img width="2144" alt="bob-i1"
src="https://github.com/keep-network/tbtc-v2/assets/11180469/6a32050d-6bc4-44cb-a299-1bc3e8009364">

- The **green** contracts are existing tBTC contracts that form the
current bridging flow based on the Wormhole Token Portal (see [RFC
8](https://github.com/keep-network/tbtc-v2/blob/main/docs/rfc/rfc-8.adoc#37-smart-contracts)).
The `TBTC Bridge` component is the `Bridge` contract deployed on L1
Ethereum responsible for minting the L1 TBTC token. The
`L2WormholeGateway` contract has the authority to mint canonical L2 TBTC
on the given L2, based on received Wormhole L2 TBTC tokens. The `L2TBTC`
component is the canonical L2 TBTC token contract.
- The **blue** contracts are the new contracts that enable the new
direct bridging flow. The `AbstractTBTCDepositor` contract (introduced
by #778) provides some
useful tooling facilitating integration with the tBTC `Bridge` and its
new **deposit with extra data** function (developed in
#760) which is the
foundation of the L2 direct bridging mechanism. The `L1BitcoinDepositor`
and `L2BitcoinDepositor` 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.
- The **red** contracts belong to the Wormhole protocol that handles
cross-chain operations. In the context of the direct bridging mechanism,
that means the transfer of minted L1 TBTC to the L2 chain. The
`TokenBridge` contract handles the bookkeeping part of the transfer. The
`Relayer` contract handles the actual execution of it.
- The **yellow** off-chain relayer bot is the planned component
(implemented as an immediate next step) that will "turn the crank" of
the direct bridging mechanism. It will initialize deposits on the L1
chain (based on L2 events in the first iteration) and finalize them once
L1 TBTC is minted by the tBTC `Bridge` contract.

The above components interact with each other in the following way:
1. The user makes the BTC deposit funding transaction and calls the
`L2BitcoinDepositor.initializeDeposit` function to initiate the deposit
process on L2 (the call is made through a dApp and tBTC SDK).
2. The off-chain relayer bot observes the `DepositInitialized` event
emitted by the `L2BitcoinDepositor` contract.
3. After assessing the deposit validity, the off-chain relayer bot calls
the `L1BitcoinDepositor.initializeDeposit` function on L1.
4. The `L1BitcoinDepositor` contract calls the
`Bridge.revealDepositWithExtra` function under the hood (through the
`AbstractTBTCDepositor` abstract contract).
5. After some time (several hours), the `Bridge` contract mints L1 TBTC
to the `L1BitcoinDepositor` contract.
6. The off-chain bot observes the mint event and calls
`L1BitcoinDepositor.finalizeDeposit` to start the finalization process
and move L1 TBTC to the L2 chain.
7. The `L1BitcoinDepositor` calls Wormhole's
`TokenBridge.transferTokensWithPayload` function to initiate the
cross-chain transfer of L1 TBTC. This call pulls out the L1 TBTC from
the `L1BitcoinDepositor` contract and locks it in the `TokenBridge`.
8. The `L1BitcoinDepositor` calls Wormhole's `Relay.sendVaasToEvm` to
send a cross-chain message to `L2BitcoinDepositor` and notify it about a
pending cross-chain transfer.
9. The Wormhole protocol calls the
`L2BitcoinDepositor.receiveWormholeMessages` function to deliver the
cross-chain message.
10. The `L2BitcoinDepositor` contract calls the
`L2WormholeGateway.receiveTbtc` function under the hood. It passes the
VAA representing the cross-chain transfer as an argument of the call.
11. The `L2WormholeGateway` uses the obtained VAA to finalize the
cross-chain transfer by calling the Wormhole's
`TokenBridge.completeTransferWithPayload` function. This call redeems
Wormhole-wrapped L2 TBTC from the `TokenBridge`.
12. The `L2WormholeGateway` uses obtained Wormhole-wrapped L2 TBTC to
call `L2TBTC.mint` and mint canonical L2 TBTC.
13. Minted canonical L2 TBTC is transferred to the L2 user.

### 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:
- Expose the L2 direct bridging feature in the tBTC Typescript SDK. This
feature will be incrementally exposed for specific L2 chains the
mechanism will be deployed for. The first L2 chain will be Base.
- Implement the off-chain relayer bot

### 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:

<img width="2144" alt="bob-i2"
src="https://github.com/keep-network/tbtc-v2/assets/11180469/f6a01138-0354-4b41-b3f1-4f4a38be7f91">
  • Loading branch information
tomaszslabon authored Mar 13, 2024
2 parents 60c336f + b2d57ce commit 89616d5
Show file tree
Hide file tree
Showing 31 changed files with 4,594 additions and 64 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/contracts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ jobs:
- name: Install Slither
env:
SLITHER_VERSION: 0.8.3
SLITHER_VERSION: 0.9.0
run: pip3 install slither-analyzer==$SLITHER_VERSION

# We need this step because the `@keep-network/tbtc` which we update in
Expand Down
303 changes: 303 additions & 0 deletions cross-chain/base/.openzeppelin/sepolia.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
{
"manifestVersion": "3.2",
"admin": {
"address": "0xDd0007713CB99564B7835FD628A1718e8F9f9785",
"txHash": "0x078036699e010480a0d2a4c443352cf0f6311f8882dddcf0b4c6c5136bcc69b4"
},
"proxies": [
{
"address": "0x0c5e36731008f4AFC1AF5Da2C4D5E07eE4a3EB69",
"txHash": "0x5a405183332f623649fcf19f8506cf2582882d5dc2b05582e0066388ef122229",
"kind": "transparent"
}
],
"impls": {
"e1501c59bd3fc7501392b17ad132c0ef733008f7128caa90d854edd898c505ec": {
"address": "0x720Cb49A8b3c03E199075544F7f1F4d772Dd6d06",
"txHash": "0xf055a10c164376e93258e15ff257e76fc8eff5a737820237271a8fb3e3506fe4",
"layout": {
"solcVersion": "0.8.17",
"storage": [
{
"label": "bridge",
"offset": 0,
"slot": "0",
"type": "t_contract(IBridge)3414",
"contract": "AbstractTBTCDepositor",
"src": "@keep-network/tbtc-v2/contracts/integrator/AbstractTBTCDepositor.sol:95"
},
{
"label": "tbtcVault",
"offset": 0,
"slot": "1",
"type": "t_contract(ITBTCVault)3440",
"contract": "AbstractTBTCDepositor",
"src": "@keep-network/tbtc-v2/contracts/integrator/AbstractTBTCDepositor.sol:96"
},
{
"label": "__gap",
"offset": 0,
"slot": "2",
"type": "t_array(t_uint256)47_storage",
"contract": "AbstractTBTCDepositor",
"src": "@keep-network/tbtc-v2/contracts/integrator/AbstractTBTCDepositor.sol:111"
},
{
"label": "_initialized",
"offset": 0,
"slot": "49",
"type": "t_uint8",
"contract": "Initializable",
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62",
"retypedFrom": "bool"
},
{
"label": "_initializing",
"offset": 1,
"slot": "49",
"type": "t_bool",
"contract": "Initializable",
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67"
},
{
"label": "__gap",
"offset": 0,
"slot": "50",
"type": "t_array(t_uint256)50_storage",
"contract": "ContextUpgradeable",
"src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36"
},
{
"label": "_owner",
"offset": 0,
"slot": "100",
"type": "t_address",
"contract": "OwnableUpgradeable",
"src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22"
},
{
"label": "__gap",
"offset": 0,
"slot": "101",
"type": "t_array(t_uint256)49_storage",
"contract": "OwnableUpgradeable",
"src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94"
},
{
"label": "reimbursementPool",
"offset": 0,
"slot": "150",
"type": "t_contract(ReimbursementPool)2999",
"contract": "Reimbursable",
"src": "@keep-network/random-beacon/contracts/Reimbursable.sol:51"
},
{
"label": "__gap",
"offset": 0,
"slot": "151",
"type": "t_array(t_uint256)49_storage",
"contract": "Reimbursable",
"src": "@keep-network/random-beacon/contracts/Reimbursable.sol:51"
},
{
"label": "deposits",
"offset": 0,
"slot": "200",
"type": "t_mapping(t_uint256,t_enum(DepositState)3465)",
"contract": "L1BitcoinDepositor",
"src": "@keep-network/tbtc-v2/contracts/l2/L1BitcoinDepositor.sol:111"
},
{
"label": "tbtcToken",
"offset": 0,
"slot": "201",
"type": "t_contract(IERC20Upgradeable)6699",
"contract": "L1BitcoinDepositor",
"src": "@keep-network/tbtc-v2/contracts/l2/L1BitcoinDepositor.sol:113"
},
{
"label": "wormhole",
"offset": 0,
"slot": "202",
"type": "t_contract(IWormhole)5318",
"contract": "L1BitcoinDepositor",
"src": "@keep-network/tbtc-v2/contracts/l2/L1BitcoinDepositor.sol:115"
},
{
"label": "wormholeRelayer",
"offset": 0,
"slot": "203",
"type": "t_contract(IWormholeRelayer)5358",
"contract": "L1BitcoinDepositor",
"src": "@keep-network/tbtc-v2/contracts/l2/L1BitcoinDepositor.sol:116"
},
{
"label": "wormholeTokenBridge",
"offset": 0,
"slot": "204",
"type": "t_contract(IWormholeTokenBridge)5443",
"contract": "L1BitcoinDepositor",
"src": "@keep-network/tbtc-v2/contracts/l2/L1BitcoinDepositor.sol:118"
},
{
"label": "l2WormholeGateway",
"offset": 0,
"slot": "205",
"type": "t_address",
"contract": "L1BitcoinDepositor",
"src": "@keep-network/tbtc-v2/contracts/l2/L1BitcoinDepositor.sol:119"
},
{
"label": "l2ChainId",
"offset": 20,
"slot": "205",
"type": "t_uint16",
"contract": "L1BitcoinDepositor",
"src": "@keep-network/tbtc-v2/contracts/l2/L1BitcoinDepositor.sol:121"
},
{
"label": "l2BitcoinDepositor",
"offset": 0,
"slot": "206",
"type": "t_address",
"contract": "L1BitcoinDepositor",
"src": "@keep-network/tbtc-v2/contracts/l2/L1BitcoinDepositor.sol:123"
},
{
"label": "l2FinalizeDepositGasLimit",
"offset": 0,
"slot": "207",
"type": "t_uint256",
"contract": "L1BitcoinDepositor",
"src": "@keep-network/tbtc-v2/contracts/l2/L1BitcoinDepositor.sol:128"
},
{
"label": "gasReimbursements",
"offset": 0,
"slot": "208",
"type": "t_mapping(t_uint256,t_struct(GasReimbursement)3472_storage)",
"contract": "L1BitcoinDepositor",
"src": "@keep-network/tbtc-v2/contracts/l2/L1BitcoinDepositor.sol:137"
},
{
"label": "initializeDepositGasOffset",
"offset": 0,
"slot": "209",
"type": "t_uint256",
"contract": "L1BitcoinDepositor",
"src": "@keep-network/tbtc-v2/contracts/l2/L1BitcoinDepositor.sol:146"
},
{
"label": "finalizeDepositGasOffset",
"offset": 0,
"slot": "210",
"type": "t_uint256",
"contract": "L1BitcoinDepositor",
"src": "@keep-network/tbtc-v2/contracts/l2/L1BitcoinDepositor.sol:153"
}
],
"types": {
"t_address": {
"label": "address",
"numberOfBytes": "20"
},
"t_array(t_uint256)47_storage": {
"label": "uint256[47]",
"numberOfBytes": "1504"
},
"t_array(t_uint256)49_storage": {
"label": "uint256[49]",
"numberOfBytes": "1568"
},
"t_array(t_uint256)50_storage": {
"label": "uint256[50]",
"numberOfBytes": "1600"
},
"t_bool": {
"label": "bool",
"numberOfBytes": "1"
},
"t_contract(IBridge)3414": {
"label": "contract IBridge",
"numberOfBytes": "20"
},
"t_contract(IERC20Upgradeable)6699": {
"label": "contract IERC20Upgradeable",
"numberOfBytes": "20"
},
"t_contract(ITBTCVault)3440": {
"label": "contract ITBTCVault",
"numberOfBytes": "20"
},
"t_contract(IWormhole)5318": {
"label": "contract IWormhole",
"numberOfBytes": "20"
},
"t_contract(IWormholeRelayer)5358": {
"label": "contract IWormholeRelayer",
"numberOfBytes": "20"
},
"t_contract(IWormholeTokenBridge)5443": {
"label": "contract IWormholeTokenBridge",
"numberOfBytes": "20"
},
"t_contract(ReimbursementPool)2999": {
"label": "contract ReimbursementPool",
"numberOfBytes": "20"
},
"t_enum(DepositState)3465": {
"label": "enum L1BitcoinDepositor.DepositState",
"members": [
"Unknown",
"Initialized",
"Finalized"
],
"numberOfBytes": "1"
},
"t_mapping(t_uint256,t_enum(DepositState)3465)": {
"label": "mapping(uint256 => enum L1BitcoinDepositor.DepositState)",
"numberOfBytes": "32"
},
"t_mapping(t_uint256,t_struct(GasReimbursement)3472_storage)": {
"label": "mapping(uint256 => struct L1BitcoinDepositor.GasReimbursement)",
"numberOfBytes": "32"
},
"t_struct(GasReimbursement)3472_storage": {
"label": "struct L1BitcoinDepositor.GasReimbursement",
"members": [
{
"label": "receiver",
"type": "t_address",
"offset": 0,
"slot": "0"
},
{
"label": "gasSpent",
"type": "t_uint96",
"offset": 20,
"slot": "0"
}
],
"numberOfBytes": "32"
},
"t_uint16": {
"label": "uint16",
"numberOfBytes": "2"
},
"t_uint256": {
"label": "uint256",
"numberOfBytes": "32"
},
"t_uint8": {
"label": "uint8",
"numberOfBytes": "1"
},
"t_uint96": {
"label": "uint96",
"numberOfBytes": "12"
}
}
}
}
}
}
Loading

0 comments on commit 89616d5

Please sign in to comment.