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

update WrappedErc20 #3

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
42 changes: 35 additions & 7 deletions contracts/WrappedERC20.sol
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/// @title Wrapped ERC20
/// @notice Represents a token on another chain
/// @dev Can be minted and burned only by the bridge
contract WrappedERC20 is ERC20 {
address public immutable bridge;
/// @dev Can be minted only by the new bridge but burned by any of old/new bridge
contract WrappedERC20 is ERC20, ERC20Permit, Ownable {
using SafeERC20 for IERC20;
address public currentBridge;
address[] public bridges;
uint8 private immutable _tokenDecimals;

/// @param _bridge responsible for minting and burning the wrapped token
/// @param _name wrapped token name
/// @param _symbol wrapped token symbol
/// @param _decimals number of decimals of the original token
constructor(address _bridge, string memory _name, string memory _symbol, uint8 _decimals) ERC20(_name, _symbol) {
constructor(address _bridge, string memory _name, string memory _symbol, uint8 _decimals) ERC20(_name, _symbol) ERC20Permit(_name) {
require(_bridge != address(0), "WrappedERC20: invalid bridge");

bridge = _bridge;
currentBridge = _bridge;
bridges.push(_bridge);
_tokenDecimals = _decimals;
}

modifier onlyCurrentBridge() {
require(msg.sender == currentBridge, "WrappedERC20: caller is not the bridge");
_;
}

modifier onlyBridge() {
require(msg.sender == bridge, "WrappedERC20: caller is not the bridge");
bool isBridge = false;
for (uint i = 0; i < bridges.length; i++) {
if (msg.sender == bridges[i]) {
isBridge = true;
break;
}
}
require(isBridge, "WrappedERC20: caller is not the bridge");
_;
}

Expand All @@ -35,7 +53,7 @@ contract WrappedERC20 is ERC20 {

/// @notice Creates `amount` tokens and assigns them to `account`, increasing the total supply
/// @dev called only by the bridge
function mint(address _to, uint _amount) external virtual onlyBridge {
function mint(address _to, uint _amount) external virtual onlyCurrentBridge {
_mint(_to, _amount);
}

Expand All @@ -44,4 +62,14 @@ contract WrappedERC20 is ERC20 {
function burn(address _from, uint _amount) external virtual onlyBridge {
_burn(_from, _amount);
}

/// @notice Sets a new bridge
/// @dev called only by the owner
function upgradeBridge(address _newBridge) external virtual onlyOwner {
require(_newBridge != address(0), "WrappedERC20: invalid bridge");
require(_newBridge != currentBridge, "WrappedERC20: same bridge");
require(_newBridge != owner(), "WrappedERC20: owner cannot be the bridge");
currentBridge = _newBridge;
bridges.push(_newBridge);
}
}
1 change: 0 additions & 1 deletion hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ require("solidity-coverage");
require('hardhat-gas-reporter');
require('hardhat-deploy');
require('hardhat-deploy-ethers');
require('@openzeppelin/hardhat-upgrades');
require('./tasks');

// This is a sample Hardhat task. To learn how to create your own go to
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@
},
"dependencies": {
"@layerzerolabs/solidity-examples": "0.0.6",
"@openzeppelin/contracts": "^4.4.1"
"@openzeppelin/contracts": "^4.9.3"
},
"devDependencies": {
"@nomiclabs/hardhat-waffle": "2.0.3",
"@nomiclabs/hardhat-etherscan": "^3.1.0",
"@ethersproject/hardware-wallets": "^5.0.14",
"@layerzerolabs/prettier-plugin-solidity": "^1.0.0-beta.19",
"@nomicfoundation/hardhat-network-helpers": "^1.0.6",
"@nomiclabs/hardhat-ethers": "^2.0.3",
"ethereum-waffle": "^3.2.0",
"@nomiclabs/hardhat-etherscan": "^3.1.0",
"@nomiclabs/hardhat-waffle": "2.0.3",
"chai": "^4.3.4",
"dotenv": "^10.0.0",
"eslint": "^7.0.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.1",
"ethereum-waffle": "^3.2.0",
"ethers": "^5.5.2",
"hardhat": "^2.12.2",
"hardhat-contract-sizer": "^2.1.1",
Expand Down
38 changes: 36 additions & 2 deletions test/WrappedERC20.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { expect } = require("chai")
const { ethers } = require("hardhat")
const { utils, constants } = require("ethers")
const { generateKeyPair } = require("crypto")

describe("WrappedERC20", () => {
const name = "WTEST"
Expand All @@ -13,7 +14,8 @@ describe("WrappedERC20", () => {
let wrappedTokenFactory

beforeEach(async () => {
[owner, bridge] = await ethers.getSigners()
;[owner, bridge] = await ethers.getSigners()
newBridge = ethers.getSigner()

wrappedTokenFactory = await ethers.getContractFactory("WrappedERC20")
wrappedToken = await wrappedTokenFactory.deploy(bridge.address, name, symbol, decimals)
Expand Down Expand Up @@ -56,4 +58,36 @@ describe("WrappedERC20", () => {
expect(await wrappedToken.balanceOf(owner.address)).to.be.eq(0)
})
})
})

describe("upgrade bridge", () => {
it("reverts when bridge is addres 0", async () => {
await expect(wrappedToken.upgradeBridge(constants.AddressZero)).to.be.revertedWith("WrappedERC20: invalid bridge")
})

it("reverts when new bridge is same as old one", async () => {
await expect(wrappedToken.upgradeBridge(bridge.address)).to.be.revertedWith("WrappedERC20: same bridge")
})

it("upgrades bridge", async () => {
await wrappedToken.upgradeBridge(newBridge.address)
expect(await wrappedToken.currentBridge()).to.be.eq(newBridge.address)
expect(await wrappedToken.bridges()).to.be.eq([bridge.address, newBridge.address])
})
})

// describe("burn", () => {
// beforeEach(async () => {
// await wrappedToken.connect(bridge).mint(owner.address, amount)
// })

// it("reverts when called not by the bridge", async () => {
// await expect(wrappedToken.burn(owner.address, amount)).to.be.revertedWith("WrappedERC20: caller is not the bridge")
// })

// it("burns wrapped tokens", async () => {
// await wrappedToken.connect(bridge).burn(owner.address, amount)
// expect(await wrappedToken.totalSupply()).to.be.eq(0)
// expect(await wrappedToken.balanceOf(owner.address)).to.be.eq(0)
// })
// })
})
Loading