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 erc20 #2

Merged
merged 2 commits into from
Dec 28, 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
86 changes: 86 additions & 0 deletions contracts/token/ERC20/ERC20MintableUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import { ISelectorRoleControl, IPausable, IEcoOwnable, SelectorRoleControlUpgradeable } from "../../access/SelectorRoleControlUpgradeable.sol";

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";

import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import { ERC20BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";

interface IEcoERC20 is IERC20, IERC20Metadata, IERC20Errors {}

interface IEcoERC20_Burnable is IEcoERC20 {
function burn(uint256 amount) external;
function burnFrom(address account, uint256 amount) external;
}

interface IEcoERC20Mintable is ISelectorRoleControl, IEcoERC20 {
function mint(address to, uint256 amount) external returns (bool);
}

interface IWETH is IEcoERC20 {
function deposit() external payable;
function withdraw(uint256 amount) external;
}

abstract contract ERC20Decimal is ERC20Upgradeable {
uint8 private immutable _decimals_;

constructor(uint8 _decimals) {
_decimals_ = _decimals;
}

function decimals() public view virtual override returns (uint8) {
return _decimals_;
}
}

contract EcoERC20Mintable is
IEcoERC20Mintable,
IEcoERC20_Burnable,
SelectorRoleControlUpgradeable,
ERC20Upgradeable,
ERC20BurnableUpgradeable
{
constructor(string memory name, string memory symbol) initializer {
initEcoERC20Mintable(_msgSender(), name, symbol);
}

function initEcoERC20Mintable(address initialOwner, string memory name, string memory symbol) public initializer {
__Ownable_init(initialOwner);
__ERC20_init(name, symbol);
}

function mint(address to, uint256 amount) onlyAdmin public override returns (bool) {
_mint(to, amount);
return true;
}

function burn(uint256 amount) public override(IEcoERC20_Burnable, ERC20BurnableUpgradeable) {
return super.burn(amount);
}

function burnFrom(address account, uint256 amount) public override(IEcoERC20_Burnable, ERC20BurnableUpgradeable) {
return super.burnFrom(account, amount);
}
}

contract EcoERC20MintableDecimal is
EcoERC20Mintable,
ERC20Decimal
{
constructor(string memory name, string memory symbol, uint8 _decimals) initializer
EcoERC20Mintable(name, symbol)
ERC20Decimal(_decimals)
{}

function decimals() public view virtual
override(IERC20Metadata, ERC20Upgradeable, ERC20Decimal) returns (uint8) {
return super.decimals();
}
}
31 changes: 31 additions & 0 deletions contracts/token/ERC20/IERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)

pragma solidity ^0.8.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import { ISelectorRoleControl } from "../../access/SelectorRoleControlUpgradeable.sol";

interface IWETH is IERC20, IERC20Metadata {
function deposit() external payable;
function withdraw(uint256 amount) external;
}

interface IERC20Burnable is IERC20, IERC20Metadata {
function burn(uint256 amount) external;
function burnFrom(address account, uint256 amount) external;
}

interface IERC20Mintable is ISelectorRoleControl, IERC20, IERC20Metadata {
function initERC20Mintable(address owner) external;
function mint(address to, uint256 amount) external;
}

interface IERC20MetadataInitializable is ISelectorRoleControl, IERC20, IERC20Metadata {
function initERC20MetadataInitializable(string memory name_, string memory symbol_, uint8 decimals_) external;
}

interface IEcoERC20 is IERC20Mintable, IERC20Burnable, IERC20MetadataInitializable {}
interface IEcoERC20Pausable is IEcoERC20 {}
80 changes: 80 additions & 0 deletions test/token/ERC20.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {
time,
loadFixture,
} from "@nomicfoundation/hardhat-toolbox/network-helpers";
import { expect } from "chai";
import { ethers } from "hardhat";

describe("ERC20 Mintable", function () {
const name = "Mintable Token";
const symbol = "M ERC20";

const amount = ethers.parseEther("100");

async function NFT_Mintable_Fixture() {
const [owner, ...users] = await ethers.getSigners();

const ERC20 = await ethers.getContractFactory("EcoERC20Mintable");
const erc20 = await ERC20.deploy(name, symbol);
// await erc20.initNFT_Mintable(owner.address, name, symbol); only for proxy

return { erc20, owner, users };
}

describe("Deployment", function () {
it("Should set the right owner", async function () {
const { erc20, owner } = await loadFixture(NFT_Mintable_Fixture);

expect(await erc20.owner()).to.equal(owner.address);
});

it("Should set the right metadata", async function () {
const { erc20 } = await loadFixture(NFT_Mintable_Fixture);

expect(await erc20.name()).to.equal(name);
expect(await erc20.symbol()).to.equal(symbol);
});
});

describe("Non Fungible Token", function () {
describe("Mint", function () {
it("Should revert with the right error if mint called from another account", async function () {
const { erc20, users } = await loadFixture(NFT_Mintable_Fixture);
const user_connected_nft = erc20.connect(users[0])
await expect( user_connected_nft.mint(users[0], amount) ).reverted;
});

it("Shouldn't fail mint with the right owner", async function () {
const { erc20, users } = await loadFixture(NFT_Mintable_Fixture);
await expect( erc20.mint(users[0], amount) ).not.reverted;
});

it("Shouldn't fail mint with the right role access account", async function () {
const { erc20, users } = await loadFixture(NFT_Mintable_Fixture);

const nextMintSelector = ethers.zeroPadBytes(erc20.mint.fragment.selector, 32)
await expect( erc20.grantRole(nextMintSelector, users[0]) ).not.reverted;

const user_connected_nft = erc20.connect(users[0])
await expect( user_connected_nft.mint(users[0], amount) ).not.reverted;

await expect( erc20.revokeRole(nextMintSelector, users[0]) ).not.reverted;
await expect( user_connected_nft.mint(users[0], amount) ).reverted;
});
});

describe("Transfer", function () {
it("Should revert with the right error if mint called from another account", async function () {
const { owner, erc20, users } = await loadFixture(NFT_Mintable_Fixture);
await expect( erc20.mint(users[0], amount) ).not.reverted;

await erc20.connect(users[0]).approve(owner, ethers.MaxUint256);
await expect( erc20.transferFrom(users[0], users[1], await erc20.balanceOf(users[0])) ).not.reverted;
await expect( erc20.transferFrom(users[0], users[1], amount) ).reverted;
await expect( erc20.connect(users[1]).transferFrom(users[1], users[0], await erc20.balanceOf(users[1])) ).reverted;
await erc20.connect(users[1]).approve(owner, ethers.MaxUint256);
await expect( erc20.transferFrom(users[1], users[0], await erc20.balanceOf(users[1])) ).not.reverted;
});
});
});
});