Skip to content

Commit

Permalink
Adding Base module for canonical TBTC
Browse files Browse the repository at this point in the history
Most of the files were copied from the Optimism module and adjusted for
the Base chain, ie. ids and addresses. There is one change that had to
be added which is a custom network for hardhat etherscan plugin. Base is
not supported by this plugin by default and a custom network
configuration had to be added.
  • Loading branch information
dimpar committed Jul 27, 2023
1 parent 93b8744 commit 6968304
Show file tree
Hide file tree
Showing 48 changed files with 14,109 additions and 0 deletions.
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, such as `arbitrumGoerli/ArbitrumWormholeGateway.json.`
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://opt-goerli.g.alchemy.com/v2/<alchemy_api_key>`
- `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

0 comments on commit 6968304

Please sign in to comment.