Skip to content
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

Adding Base module for canonical TBTC #673

Merged
merged 9 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cross-chain/base/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
artifacts/
build/
cache/
deployments/
export/
hardhat-dependency-compiler/
typechain/
export.json
18 changes: 18 additions & 0 deletions cross-chain/base/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"root": true,
"extends": ["@thesis-co"],
"parserOptions": {
"ecmaVersion": 2017,
"sourceType": "module"
},
"env": {
"es6": true,
"mocha": true
},
"rules": {
"new-cap": "off",
"import/no-extraneous-dependencies": "off",
"@typescript-eslint/no-use-before-define": "off",
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }]
}
}
15 changes: 15 additions & 0 deletions cross-chain/base/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Hardhat
/artifacts/
/build/
/cache/
/export/
/external/npm
/typechain/
/export.json
/deployments/*
!/deployments/mainnet/
!/deployments/base/
!/deployments/baseGoerli/

# OZ
/.openzeppelin/unknown-*.json
3 changes: 3 additions & 0 deletions cross-chain/base/.mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"require": "ts-node/register/files"
}
9 changes: 9 additions & 0 deletions cross-chain/base/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.openzeppelin/
artifacts/
build/
cache/
deployments/
export/
hardhat-dependency-compiler/
typechain/
export.json
11 changes: 11 additions & 0 deletions cross-chain/base/.prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
...require("@thesis-co/prettier-config"),
overrides: [
{
files: "*.sol",
options: {
tabWidth: 4,
},
},
],
}
7 changes: 7 additions & 0 deletions cross-chain/base/.solhint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "keep",
"plugins": [],
"rules": {
"func-visibility": ["error", { "ignoreConstructors": true }]
}
}
2 changes: 2 additions & 0 deletions cross-chain/base/.solhintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
hardhat-dependency-compiler/
node_modules/
1 change: 1 addition & 0 deletions cross-chain/base/.tsconfig-eslint.json
59 changes: 59 additions & 0 deletions cross-chain/base/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
:toc: macro

= Threshold cross-chain - Base

This package brings Bitcoin to Ethereum L2 Base. For more details please
see link:https://github.com/keep-network/tbtc-v2/blob/main/docs/rfc/rfc-8.adoc[RFC 8: Cross-chain Tokenized Threshold BTC]

== How it works?

```
+----------------------------+ +-------------------------------------------------------------------+
| Ethereum | | Base |
| | | |
| +----------------------+ | | +----------------------+ +---------------------+ +----------+ |
| | Wormhole TokenBridge |--|---------|--| Wormhole TokenBridge |--| BaseWormholeGateway |--| BaseTBTC | |
| +----------------------+ | | +----------------------+ +---------------------+ +----------+ |
| | | |
+----------------------------+ +-------------------------------------------------------------------+
```

- `BaseTBTC` canonical tBTC token on Base with a minting authority
delegated to `BaseWormholeGateway`.
- `BaseWormholeGateway` is a smart contract wrapping and unwrapping
Wormhole-specific tBTC representation into the canonical `BaseTBTC` token.

=== Updating Wormhole Gateway mapping

The deployment scripts are responsible for managing updates of the tBTC gateway
addresses across various chains. These addresses are stored in the `external/`
directory for a specific network.
It is important to note that these addresses should remain constant for the
mainnet network. However, there may be instances where a new version of a
cross-chain module is deployed to the testing network, which would require a
manual update of the corresponding address.

=== Deploy contracts

To deploy all contracts on the given network, please run:
```
yarn deploy --network <network>
```

Supported networks:
- `hardhat` - for local development
- `baseGoerli` - L2 testing network
- `base` - L2 mainnet

Currently, this module does not deploy any contracts on L1. All the existing
Wormhole contract addresses that are used in this module are stored under
`external/<network>` dir.

If contracts haven't been built yet or changes occurred, `deploy` task will build
the contracts before running the deployment script. This command produces
an `export.json` file containing contract deployment info. Note that for the
chains other than `hardhat` the following environment variables are needed:

- `L2_CHAIN_API_URL` - URL to access blockchain services, e.g. `https://goerli.base.org`
- `L2_ACCOUNTS_PRIVATE_KEYS` - Private keys for the deployer and council `<0xOwnerPrivKey,0xCouncilPrivKey>`
- `BASESCAN_API_KEY` - Base Etherscan API key
18 changes: 18 additions & 0 deletions cross-chain/base/contracts/test/BaseTBTCUpgraded.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.17;

import "@keep-network/tbtc-v2/contracts/l2/L2TBTC.sol";

/// @notice Canonical tBTC Token on Base - upgraded version.
/// @dev This contract is intended solely for testing purposes. As it currently
/// stands in the implementation of L2TBTC.sol, there are no reserved
/// storage gap slots available, thereby limiting the upgradability to a
/// child contract only.
contract BaseTBTCUpgraded is L2TBTC {
string public newVar;

function initializeV2(string memory _newVar) public {
newVar = _newVar;
}
}
18 changes: 18 additions & 0 deletions cross-chain/base/contracts/test/BaseWormholeGatewayUpgraded.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.17;

import "@keep-network/tbtc-v2/contracts/l2/L2WormholeGateway.sol";

/// @notice Wormhole gateway for L2 Base - upgraded version.
/// @dev This contract is intended solely for testing purposes. As it currently
/// stands in the implementation of L2WormholeGateway.sol, there are no
/// reserved storage gap slots available, thereby limiting the upgradability
/// to a child contract only.
contract BaseWormholeGatewayUpgraded is L2WormholeGateway {
string public newVar;

function initializeV2(string memory _newVar) public {
newVar = _newVar;
}
}
28 changes: 28 additions & 0 deletions cross-chain/base/deploy_l2/00_resolve_arbitrum_wormhole_gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { HardhatRuntimeEnvironment } from "hardhat/types"
import type { DeployFunction } from "hardhat-deploy/types"

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { helpers, deployments } = hre
const { log } = deployments

const ArbitrumWormholeGateway = await deployments.getOrNull(
"ArbitrumWormholeGateway"
)

if (
ArbitrumWormholeGateway &&
helpers.address.isValid(ArbitrumWormholeGateway.address)
) {
log(
`using existing ArbitrumWormholeGateway at ${ArbitrumWormholeGateway.address}`
)
} else if (hre.network.name === "hardhat") {
log("using fake ArbitrumWormholeGateway for hardhat network")
} else {
throw new Error("deployed ArbitrumWormholeGateway contract not found")
}
}

export default func

func.tags = ["ArbitrumWormholeGateway"]
21 changes: 21 additions & 0 deletions cross-chain/base/deploy_l2/00_resolve_base_token_bridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { HardhatRuntimeEnvironment } from "hardhat/types"
import type { DeployFunction } from "hardhat-deploy/types"

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { helpers, deployments } = hre
const { log } = deployments

const BaseTokenBridge = await deployments.getOrNull("BaseTokenBridge")

if (BaseTokenBridge && helpers.address.isValid(BaseTokenBridge.address)) {
log(`using existing Base TokenBridge at ${BaseTokenBridge.address}`)
} else if (hre.network.name === "hardhat") {
log("using fake Base TokenBridge for hardhat network")
} else {
throw new Error("deployed Base TokenBridge contract not found")
}
}

export default func

func.tags = ["BaseTokenBridge"]
21 changes: 21 additions & 0 deletions cross-chain/base/deploy_l2/00_resolve_base_wormhole_tbtc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { HardhatRuntimeEnvironment } from "hardhat/types"
import type { DeployFunction } from "hardhat-deploy/types"

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { helpers, deployments } = hre
const { log } = deployments

const BaseWormholeTBTC = await deployments.getOrNull("BaseWormholeTBTC")

if (BaseWormholeTBTC && helpers.address.isValid(BaseWormholeTBTC.address)) {
log(`using existing Base WormholeTBTC at ${BaseWormholeTBTC.address}`)
} else if (hre.network.name === "hardhat") {
log("using fake Base WormholeTBTC for hardhat network")
} else {
throw new Error("deployed Base WormholeTBTC contract not found")
}
}

export default func

func.tags = ["BaseWormholeTBTC"]
28 changes: 28 additions & 0 deletions cross-chain/base/deploy_l2/00_resolve_optimism_wormhole_gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { HardhatRuntimeEnvironment } from "hardhat/types"
import type { DeployFunction } from "hardhat-deploy/types"

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { helpers, deployments } = hre
const { log } = deployments

const OptimismWormholeGateway = await deployments.getOrNull(
"OptimismWormholeGateway"
)

if (
OptimismWormholeGateway &&
helpers.address.isValid(OptimismWormholeGateway.address)
) {
log(
`using existing OptimismWormholeGateway at ${OptimismWormholeGateway.address}`
)
} else if (hre.network.name === "hardhat") {
log("using fake OptimismWormholeGateway for hardhat network")
} else {
throw new Error("deployed OptimismWormholeGateway contract not found")
}
}

export default func

func.tags = ["OptimismWormholeGateway"]
28 changes: 28 additions & 0 deletions cross-chain/base/deploy_l2/00_resolve_polygon_wormhole_gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { HardhatRuntimeEnvironment } from "hardhat/types"
import type { DeployFunction } from "hardhat-deploy/types"

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { helpers, deployments } = hre
const { log } = deployments

const PolygonWormholeGateway = await deployments.getOrNull(
"PolygonWormholeGateway"
)

if (
PolygonWormholeGateway &&
helpers.address.isValid(PolygonWormholeGateway.address)
) {
log(
`using existing PolygonWormholeGateway at ${PolygonWormholeGateway.address}`
)
} else if (hre.network.name === "hardhat") {
log("using fake PolygonWormholeGateway for hardhat network")
} else {
throw new Error("deployed PolygonWormholeGateway contract not found")
}
}

export default func

func.tags = ["PolygonWormholeGateway"]
36 changes: 36 additions & 0 deletions cross-chain/base/deploy_l2/01_deploy_base_tbtc_token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { HardhatRuntimeEnvironment } from "hardhat/types"
import type { DeployFunction } from "hardhat-deploy/types"

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { ethers, getNamedAccounts, helpers } = hre
const { deployer } = await getNamedAccounts()

const [, proxyDeployment] = await helpers.upgrades.deployProxy("BaseTBTC", {
contractName: "@keep-network/tbtc-v2/contracts/l2/L2TBTC.sol:L2TBTC",
initializerArgs: ["Base tBTC v2", "tBTC"],
factoryOpts: { signer: await ethers.getSigner(deployer) },
proxyOpts: {
kind: "transparent",
},
})

// TODO: Investigate the possibility of adding Tenderly verification for
// L2 and upgradable proxy.

// Contracts can be verified on L2 Base Etherscan in a similar way as we
// do it on L1 Etherscan
if (hre.network.tags.basescan) {
// We use `verify` instead of `verify:verify` as the `verify` task is defined
// in "@openzeppelin/hardhat-upgrades" to verify the proxy’s implementation
// contract, the proxy itself and any proxy-related contracts, as well as
// link the proxy to the implementation contract’s ABI on (Ether)scan.
await hre.run("verify", {
address: proxyDeployment.address,
constructorArgsParams: proxyDeployment.args,
})
}
}

export default func

func.tags = ["BaseTBTC"]
Loading