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: standardize ERC1155Common contract #21

Merged
merged 9 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
23 changes: 10 additions & 13 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ on:
branches:
- main
- dev
- 'feature/*'
- 'features/*'
- "feature/*"
- "features/*"
pull_request:
branches:
- main
- dev
- 'feature/*'
- 'features/*'
- "feature/*"
- "features/*"

env:
FOUNDRY_PROFILE: ci
Expand All @@ -23,24 +23,21 @@ jobs:
fail-fast: true

name: Foundry project
runs-on: [self-hosted, dockerize]
runs-on: ubuntu-latest
steps:
- id: 'gh-app'
name: 'Get Token'
uses: 'tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a' #v1.7.0
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_PRIVATE_KEY }}

- uses: actions/[email protected]
with:
submodules: recursive
token: ${{ steps.gh-app.outputs.token }}

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Install dependencies
run: |
forge install
id: install

- name: Run Forge build
run: |
Expand Down
3 changes: 2 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
forge-std/=lib/forge-std/src/
ds-test/=lib/forge-std/lib/ds-test/src/
@openzeppelin/=./node_modules/@openzeppelin/
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts
143 changes: 106 additions & 37 deletions src/ERC1155Common.sol
Original file line number Diff line number Diff line change
@@ -1,80 +1,149 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;

import { AccessControlEnumerable } from "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";

import { ERC1155Burnable } from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol";
import { ERC1155Pausable } from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Pausable.sol";
import { ERC1155Supply } from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
import { AccessControlEnumerable } from "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { IERC1155Common } from "./interfaces/IERC1155Common.sol";

contract ERC1155Common is AccessControlEnumerable, ERC1155, ERC1155Supply, IERC1155Common {
contract ERC1155Common is
ERC1155,
AccessControlEnumerable,
ERC1155Pausable,
ERC1155Burnable,
ERC1155Supply,
IERC1155Common
{
using Strings for uint256;

bytes32 public constant URI_SETTER_ROLE = keccak256("URI_SETTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

string private _name;
string private _symbol;

constructor(string memory name_, string memory symbol_, string memory baseTokenURI, address[] memory minters)
payable
ERC1155(baseTokenURI)
{
constructor(address admin, string memory name_, string memory symbol_, string memory uri_) ERC1155(uri_) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(PAUSER_ROLE, admin);
_grantRole(MINTER_ROLE, admin);
_grantRole(URI_SETTER_ROLE, admin);

huyhuynh3103 marked this conversation as resolved.
Show resolved Hide resolved
_name = name_;
_symbol = symbol_;
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
bytes32 minterRole = MINTER_ROLE;

uint256 length = minters.length;
for (uint256 i; i < length;) {
_grantRole(minterRole, minters[i]);
unchecked {
++i;
}
}
}

function uri(uint256 tokenId) public view override returns (string memory) {
string memory _uri = super.uri(tokenId);
return string(abi.encodePacked(_uri, tokenId.toString()));
}

function name() external view returns (string memory) {
return _name;
/**
* @dev Set the URI for all token types.
* Requirements:
* - the caller must have the `URI_SETTER_ROLE`.
*/
huyhuynh3103 marked this conversation as resolved.
Show resolved Hide resolved
function setURI(string memory newURI) external onlyRole(URI_SETTER_ROLE) {
_setURI(newURI);
}

function symbol() external view returns (string memory) {
return _symbol;
/**
* @dev Pauses all token transfers.
* Requirements:
* - the caller must have the `PAUSER_ROLE`.
*/
function pause() external onlyRole(PAUSER_ROLE) {
_pause();
}

/**
* @dev Sets the base URI for metadata of ERC1155 tokens.
* @param uri_ The new base URI.
* @dev Unpauses all token transfers.
* Requirements:
* - the caller must have the `PAUSER_ROLE`.
*/
function setURI(string calldata uri_) external onlyRole(DEFAULT_ADMIN_ROLE) {
_setURI(uri_);
function unpause() external onlyRole(PAUSER_ROLE) {
_unpause();
}

/// @inheritdoc IERC1155Common
function mint(address to, uint256 id, uint256 amount) external onlyRole(MINTER_ROLE) {
_mint(to, id, amount, "");
function mint(address account, uint256 id, uint256 amount, bytes calldata data) public virtual onlyRole(MINTER_ROLE) {
_mint(account, id, amount, data);
}

/// @inheritdoc IERC1155Common
function batchMint(address to, uint256[] calldata ids, uint256[] calldata amounts) external onlyRole(MINTER_ROLE) {
_mintBatch(to, ids, amounts, "");
function mintBatch(address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data)
public
virtual
onlyRole(MINTER_ROLE)
{
_mintBatch(to, ids, amounts, data);
}

/**
* @dev Mint single token to multiple addresses.
* Requirements:
* - the caller must have the `MINTER_ROLE`.
*/
function bulkMint(uint256 id, address[] calldata tos, uint256[] calldata amounts, bytes[] calldata datas)
public
virtual
onlyRole(MINTER_ROLE)
{
uint256 length = tos.length;
require(length == amounts.length, "ERC1155: invalid array lengths");
huyhuynh3103 marked this conversation as resolved.
Show resolved Hide resolved
require(length == datas.length, "ERC1155: invalid array lengths");

for (uint256 i; i < length; ++i) {
_mint(tos[i], id, amounts[i], datas[i]);
}
}

/**
* @dev See {ERC1155-uri}.
*/
function uri(uint256 tokenId) public view override returns (string memory) {
huyhuynh3103 marked this conversation as resolved.
Show resolved Hide resolved
string memory uri_ = super.uri(tokenId);
return string.concat(uri_, tokenId.toString());
}

/**
* @dev Collection name.
*/
function name() public view returns (string memory) {
huyhuynh3103 marked this conversation as resolved.
Show resolved Hide resolved
return _name;
}

/**
* @dev Collection symbol.
*/
function symbol() public view returns (string memory) {
huyhuynh3103 marked this conversation as resolved.
Show resolved Hide resolved
return _symbol;
}

function supportsInterface(bytes4 interfaceId) public view override(ERC1155, AccessControlEnumerable) returns (bool) {
/**
* @dev See {ERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC1155, AccessControlEnumerable)
returns (bool)
{
return interfaceId == type(IERC1155Common).interfaceId || super.supportsInterface(interfaceId);
}

/**
* @dev See {ERC1155-_beforeTokenTransfer}.
*/
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual override(ERC1155, ERC1155Supply) {
) internal virtual override(ERC1155, ERC1155Pausable, ERC1155Supply) {
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
}
}
2 changes: 1 addition & 1 deletion src/ERC721Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import "./interfaces/IERC721Common.sol";
import "./refs/ERC721Nonce.sol";
import "./ERC721PresetMinterPauserAutoIdCustomized.sol";

abstract contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized, IERC721State, IERC721Common {
contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized, IERC721State, IERC721Common {
constructor(string memory name, string memory symbol, string memory baseTokenURI)
ERC721PresetMinterPauserAutoIdCustomized(name, symbol, baseTokenURI)
{ }
Expand Down
26 changes: 24 additions & 2 deletions src/interfaces/IERC1155Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,39 @@ pragma solidity ^0.8.0;
interface IERC1155Common {
huyhuynh3103 marked this conversation as resolved.
Show resolved Hide resolved
/**
* @dev Mints a single ERC1155 token and assigns it to the specified address.
*
* Requirements:
* - the caller must have the `MINTER_ROLE`.
*
* @param to The address to which the minted token will be assigned.
* @param id The ID of the token to mint.
* @param amount The amount of tokens to mint.
* @param data Additional data with no specified format.
*/
function mint(address to, uint256 id, uint256 amount) external;
function mint(address to, uint256 id, uint256 amount, bytes calldata data) external;

/**
* @dev Mints multiple ERC1155 tokens and assigns them to the specified address.
*
* Requirements:
* - the caller must have the `MINTER_ROLE`.
*
* @param to The address to which the minted tokens will be assigned.
* @param ids The IDs of the tokens to mint.
* @param amounts The amounts of tokens to mint.
* @param data Additional data with no specified format.
*/
function batchMint(address to, uint256[] calldata ids, uint256[] calldata amounts) external;
function mintBatch(address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;

/**
* @dev Mint single token to multiple addresses.
* Requirements:
* - the caller must have the `MINTER_ROLE`.
*
* @param id The ID of the token to mint.
* @param tos The addresses to which the minted tokens will be assigned.
* @param amounts The amounts of tokens to mint.
* @param datas Additional data with no specified format.
*/
function bulkMint(uint256 id, address[] calldata tos, uint256[] calldata amounts, bytes[] calldata datas) external;
}
6 changes: 3 additions & 3 deletions src/mock/SampleERC1155.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.0;
import "../ERC1155Common.sol";

contract SampleERC1155 is ERC1155Common {
constructor(string memory name, string memory symbol, string memory baseTokenURI, address[] memory minters)
ERC1155Common(name, symbol, baseTokenURI, minters)
{}
constructor(address admin, string memory name, string memory symbol, string memory uri)
ERC1155Common(admin, name, symbol, uri)
{ }
}
10 changes: 5 additions & 5 deletions test/foundry/SampleERC1155.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ contract SampleERC1155Test is Test {
string public constant NAME = "SampleERC1155";
string public constant SYMBOL = "NFT1155";
string public constant BASE_URI = "http://example.com/";
address[] public minters = [address(1)];
address admin = makeAddr("admin");

ERC1155Common internal _t;

function setUp() public virtual {
_t = new SampleERC1155(NAME, SYMBOL, BASE_URI, minters);
_t = new SampleERC1155(admin, NAME, SYMBOL, BASE_URI);
}

function testName() public virtual {
Expand All @@ -36,12 +36,12 @@ contract SampleERC1155Test is Test {
}

function testMint() public virtual {
vm.startPrank(address(1));
_token().mint(address(15), 15, 15);
vm.startPrank(admin);
_token().mint(address(15), 15, 15, "");
assertEq(_token().totalSupply(15), 15);
assertEq(_token().balanceOf(address(15), 15), 15);

_token().mint(address(20), 15, 15);
_token().mint(address(20), 15, 15, "");
assertEq(_token().totalSupply(15), 30);
assertEq(_token().balanceOf(address(20), 15), 15);
vm.stopPrank();
Expand Down