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

feat(governance): bridge UDT on ARB and OP chains #13521

Merged
merged 37 commits into from Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a652291
add erc20 oeth bridge contract
clemsos Mar 25, 2024
5081d81
deploy script
clemsos Mar 25, 2024
a4c3b51
add fixes
clemsos Mar 26, 2024
9cab5bd
test deploymeny manifest
clemsos Mar 26, 2024
16fa589
pass signer to getErc20 helper
clemsos Mar 26, 2024
eddd6f9
add scripts
clemsos Mar 26, 2024
022350b
add optimsim sdk dep
clemsos Mar 26, 2024
335476e
better logs
clemsos Mar 26, 2024
cb40ace
balances log
clemsos Mar 26, 2024
89a74f5
scrpt to deploy bridged token on OP
clemsos Mar 27, 2024
6901fb3
delete custom bridge contract
clemsos Mar 27, 2024
1f39bfd
move deploy script to governance
clemsos Mar 27, 2024
a66bb92
remove refs to OP sepolia
clemsos Mar 27, 2024
c8936a1
cleanup gitignore
clemsos Mar 29, 2024
63a0b6d
cleanup and use values from networks package
clemsos Mar 29, 2024
5c754d8
Merge branch 'master' into oeth-l2-bridge
clemsos Mar 29, 2024
4cf77f3
arb bridge contract
clemsos Mar 29, 2024
4ef2a27
move scripts to bridge folder
clemsos Mar 29, 2024
8c9d772
Merge branch 'oeth-l2-bridge' of github.com:unlock-protocol/unlock in…
clemsos Mar 29, 2024
e44552e
arb use mainnet
clemsos Mar 29, 2024
3cb1c03
check if token already exists
clemsos Mar 29, 2024
f3f4910
add arbitrum addresses
clemsos Mar 29, 2024
8795f14
OP default to mainnet
clemsos Mar 29, 2024
2d01eba
add optimism bridged token
clemsos Mar 29, 2024
1f359be
pass correct ERC20 abi
clemsos Mar 29, 2024
7248175
deploy on base
clemsos Mar 29, 2024
f2f55b7
add base token bridge
clemsos Mar 29, 2024
4bae6d2
basic base bridge token script
clemsos Mar 29, 2024
64dc82f
Merge branch 'master' into oeth-l2-bridge
clemsos Apr 3, 2024
6fd475e
rm unwanted git ref
clemsos Apr 3, 2024
572fead
move all udt scripts to governance
clemsos Apr 3, 2024
38f8e2b
remove changes from smart-contracts folder
clemsos Apr 3, 2024
8f3ca97
unchecked sepola deployments
clemsos Apr 3, 2024
8df8e3e
deploy UDT on base sepolia
clemsos Apr 3, 2024
1ab9724
improve base script
clemsos Apr 3, 2024
ceede74
add gas estimate
clemsos Apr 4, 2024
2d8a994
Merge branch 'master' of github.com:unlock-protocol/unlock into oeth-…
clemsos Apr 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion governance/.gitignore
Expand Up @@ -7,4 +7,6 @@ artifacts
zk-artifacts
zk-cache
artifacts-zk
cache-zk
cache-zk

!contracts/l2/UDTOptimism.sol
12 changes: 11 additions & 1 deletion governance/.openzeppelin/sepolia.json
Expand Up @@ -19,6 +19,16 @@
"address": "0x447B1492C5038203f1927eB2a374F5Fcdc25999d",
"txHash": "0xa552ab865e287870a8c75bca97243b7cdc1a73a60e3221c6198ffaec2cd155fa",
"kind": "transparent"
},
{
"address": "0xAF7B2b27274fa132728a26f611aAF589AB6d4f31",
"txHash": "0x73f52631491158978dee1c0d2ba69b3136d1d76b73b5b69eaadc59e0ed233bca",
"kind": "transparent"
},
{
"address": "0x0B26203E3DE7E680c9749CFa47b7ea37fEE7bd98",
"txHash": "0xed5394f06743a9998e02ec842e0d0f1a7a18ca91a3ea307a9cd2c328d213ecce",
"kind": "transparent"
}
],
"impls": {
Expand Down Expand Up @@ -1018,4 +1028,4 @@
}
}
}
}
}
115 changes: 115 additions & 0 deletions governance/contracts/l2/UDTOptimism.sol
clemsos marked this conversation as resolved.
Show resolved Hide resolved
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/// @title IOptimismMintableERC20
/// @notice This interface is available on the OptimismMintableERC20 contract.
/// We declare it as a separate interface so that it can be used in
/// custom implementations of OptimismMintableERC20.
interface IOptimismMintableERC20 is IERC165 {
function remoteToken() external view returns (address);

function bridge() external returns (address);

function mint(address _to, uint256 _amount) external;

function burn(address _from, uint256 _amount) external;
}

contract UDTOptimism is IOptimismMintableERC20, ERC20 {
/// @notice Address of the corresponding version of this token on the remote chain.
address public immutable REMOTE_TOKEN;

/// @notice Address of the StandardBridge on this network.
address public immutable BRIDGE;

/// @notice Emitted whenever tokens are minted for an account.
/// @param account Address of the account tokens are being minted for.
/// @param amount Amount of tokens minted.
event Mint(address indexed account, uint256 amount);

/// @notice Emitted whenever tokens are burned from an account.
/// @param account Address of the account tokens are being burned from.
/// @param amount Amount of tokens burned.
event Burn(address indexed account, uint256 amount);

/// @notice A modifier that only allows the bridge to call.
modifier onlyBridge() {
require(
msg.sender == BRIDGE,
"UnlockDiscountToken: only bridge can mint and burn"
);
_;
}

/// @param _bridge Address of the L2 standard bridge.
/// @param _remoteToken Address of the corresponding L1 token.
/// @param _name ERC20 name.
/// @param _symbol ERC20 symbol.
constructor(
address _bridge,
address _remoteToken,
string memory _name,
string memory _symbol
) ERC20(_name, _symbol) {
REMOTE_TOKEN = _remoteToken;
BRIDGE = _bridge;
}

/// @custom:legacy
/// @notice Legacy getter for REMOTE_TOKEN.
function remoteToken() public view returns (address) {
return REMOTE_TOKEN;
}

/// @custom:fix
/// @notice Getter for REMOTE_TOKEN required by @eth-optimism/sdk
function l1Token() public view returns (address) {
return REMOTE_TOKEN;
}

/// @custom:legacy
/// @notice Legacy getter for BRIDGE.
function bridge() public view returns (address) {
return BRIDGE;
}

/// @custom:fix
/// @notice Getter for REMOTE_TOKEN required by @eth-optimism/sdk
function l2Bridge() public view returns (address) {
return BRIDGE;
}

/// @notice ERC165 interface check function.
/// @param _interfaceId Interface ID to check.
/// @return Whether or not the interface is supported by this contract.
function supportsInterface(
bytes4 _interfaceId
) external pure virtual returns (bool) {
bytes4 iface1 = type(IERC165).interfaceId;
// Interface corresponding to the updated OptimismMintableERC20 (this contract).
bytes4 iface2 = type(IOptimismMintableERC20).interfaceId;
return _interfaceId == iface1 || _interfaceId == iface2;
}

/// @notice Allows the StandardBridge on this network to mint tokens.
/// @param _to Address to mint tokens to.
/// @param _amount Amount of tokens to mint.
function mint(
address _to,
uint256 _amount
) external virtual override(IOptimismMintableERC20) onlyBridge {
_mint(_to, _amount);
emit Mint(_to, _amount);
}

/// @notice Prevents tokens from being withdrawn to L1.
function burn(
address,
uint256
) external virtual override(IOptimismMintableERC20) onlyBridge {
revert("UnlockDiscountToken cannot be withdrawn");
clemsos marked this conversation as resolved.
Show resolved Hide resolved
}
}
29 changes: 29 additions & 0 deletions governance/scripts/deployments/oeth-bridged-token.js
@@ -0,0 +1,29 @@
const {
deployContract,
getNetwork,
} = require('@unlock-protocol/hardhat-helpers')

async function main({
bridge = '0x4200000000000000000000000000000000000010',
remoteToken = '0x0B26203E3DE7E680c9749CFa47b7ea37fEE7bd98', // UDT on Sepolia
tokenName = 'UDT (Bridged)',
tokenSymbol = 'UDT.b',
} = {}) {
const { name, id } = await getNetwork()
// copyAndBuildContractsAtVersion,

console.log(
`Deploying ${tokenName} (${tokenSymbol}) bridge contract on ${name} (${id})`
)

const deployArgs = [bridge, remoteToken, tokenName, tokenSymbol]
await deployContract('contracts/l2/UDTOptimism.sol:UDTOptimism', deployArgs)
}

main()
.then(() => process.exit(0))
.catch((error) => {
// eslint-disable-next-line no-console
console.error(error)
process.exit(1)
})
6 changes: 4 additions & 2 deletions packages/hardhat-helpers/src/fork.js
Expand Up @@ -222,12 +222,14 @@ const getDelegates = async () => {
return await Promise.all(delegates.map((delegate) => impersonate(delegate)))
}

const getERC20Contract = async (tokenAddress) => {
const getERC20Contract = async (tokenAddress, signer) => {
const { ethers } = require('hardhat')
const {
nativeCurrency: { wrapped },
} = await getNetwork()
const [signer] = await ethers.getSigners()
if (!signer) {
;[signer] = await ethers.getSigners()
}
return tokenAddress === wrapped
? await ethers.getContractAt(WETH_ABI, wrapped, signer)
: await ethers.getContractAt(ERC20_ABI, tokenAddress, signer)
Expand Down
1 change: 1 addition & 0 deletions smart-contracts/package.json
Expand Up @@ -9,6 +9,7 @@
},
"dependencies": {
"@connext/interfaces": "2.0.5",
"@eth-optimism/sdk": "3.2.3",
"@nomiclabs/hardhat-ethers": "2.2.3",
"@nomiclabs/hardhat-etherscan": "3.1.8",
"@nomiclabs/hardhat-waffle": "2.0.6",
Expand Down
102 changes: 102 additions & 0 deletions smart-contracts/scripts/l2tokens.js
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing. This should go into governance folder. We should be able to solve the sdk ethers5 issue with yarn resolutions but I hadnt been able too so far ethereum-optimism/optimism#6295

@@ -0,0 +1,102 @@
const {
getERC20Contract,
getNetwork,
} = require('@unlock-protocol/hardhat-helpers')
const { ethers } = require('hardhat')
const optimism = require('@eth-optimism/sdk')

const L1_UDT_SEPOLIA = '0x0B26203E3DE7E680c9749CFa47b7ea37fEE7bd98'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get it from the networks package please?

const L2_UDT_OP_SEPOLIA = '0xfa7AC1c24339f629826C419eC95961Df58563438'

const OP_SEPOLIA_NETWORK = {
name: 'Op Sepolia',
id: 11155420,
provider: 'https://sepolia.optimism.io',
}

async function main({
l1TokenAddress = L1_UDT_SEPOLIA,
l2TokenAddress = L2_UDT_OP_SEPOLIA,
l1ChainId = 11155111, // Sepolia
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neworks package?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. We dont have OP Sepolia in it. I was going to add Base Sepolia in networks package so maybe use that as default after

l2ChainId = OP_SEPOLIA_NETWORK.id, // 10 for OP Mainnet
amount = 1000000000000000000n, // default to 1
} = {}) {
const { DEPLOYER_PRIVATE_KEY } = process.env

const [l1, l2] = await Promise.all([
await getNetwork(l1ChainId),
l2ChainId !== 11155420 ? await getNetwork(l2ChainId) : OP_SEPOLIA_NETWORK,
])

console.log(
`Bridging tokens from L1 ${l1.name} (${l1.id}) to L2 ${l2.name} (${l2.id})...`
)

// Create the RPC providers and wallets
const l1Provider = new ethers.providers.StaticJsonRpcProvider(l1.provider)
const l2Provider = new ethers.providers.StaticJsonRpcProvider(l2.provider)
const l1Wallet = new ethers.Wallet(DEPLOYER_PRIVATE_KEY, l1Provider)
const l2Wallet = new ethers.Wallet(DEPLOYER_PRIVATE_KEY, l2Provider)

// tokens
const l1Token = await getERC20Contract(l1TokenAddress, l1Wallet)
const l2Token = await getERC20Contract(l2TokenAddress, l2Wallet)

console.log(
`Amount: ${ethers.utils.formatUnits(
amount
)} ${await l1Token.symbol()} to ${await l2Token.symbol()} on L2...`
)

const showBalances = async () => {
console.log(
`Balance before
- l1: ${(await l1Token.balanceOf(l1Wallet.address)).toString()}
- l2: ${(await l2Token.balanceOf(l2Wallet.address)).toString()}`
)
}

// log balances
await showBalances()

// sdk init
const messenger = new optimism.CrossChainMessenger({
l1ChainId,
l2ChainId,
l1SignerOrProvider: l1Wallet,
l2SignerOrProvider: l2Wallet,
})

console.log(`Approving tokens...`)
const txApprove = await messenger.approveERC20(
l1TokenAddress,
l2TokenAddress,
amount
)
await txApprove.wait()

console.log(`Deposit tokens...`)
const txDeposit = await messenger.depositERC20(l1Token, l2Token, amount)
await txDeposit.wait()

// wait for deposit to be ready
console.log(`Wait for the deposit to be ready...`)
await messenger.waitForMessageStatus(
txDeposit.hash,
optimism.MessageStatus.RELAYED
)

// check balances after operations
console.log(`Deposit done.`)
await showBalances()
}

// execute as standalone
if (require.main === module) {
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
})
}