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: Implement ERC721CommonUpgradeable contract #12

Merged
merged 38 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
039fb4a
forge install: openzeppelin-contracts-upgradeable
huyhuynh3103 Apr 9, 2024
c20636c
feat: implement ERC721CommonUpgradeable
huyhuynh3103 Apr 9, 2024
542d571
chore: add branch of openzeppelin-contracts-upgradeable
huyhuynh3103 Apr 9, 2024
3f61357
feat: rename folder and add test
huyhuynh3103 Apr 12, 2024
eac6993
feat: remove oz
huyhuynh3103 Apr 12, 2024
bd2f6df
forge install: openzeppelin-contracts-upgradeable
huyhuynh3103 Apr 12, 2024
69b5769
feat: add deployment scripts
huyhuynh3103 Apr 15, 2024
741c81f
chore: remove OZ
huyhuynh3103 Apr 16, 2024
4d65cb2
forge install: openzeppelin-contracts-upgradeable
huyhuynh3103 Apr 16, 2024
351dab4
chore: add version in submodule
huyhuynh3103 Apr 16, 2024
817617e
chore: add ci script
huyhuynh3103 Apr 16, 2024
b5c9cfa
chore: add ci script
huyhuynh3103 Apr 16, 2024
c829b94
chore: add code-size log
huyhuynh3103 Apr 16, 2024
17f4efa
chore: add storage log
huyhuynh3103 Apr 16, 2024
7b2f732
chore: fix gitignore
huyhuynh3103 Apr 16, 2024
5037dde
chore: use custom error instead
huyhuynh3103 Apr 16, 2024
f32a541
feat: remove .DS_Store
huyhuynh3103 Apr 16, 2024
8c72ed8
chore: fix comments
huyhuynh3103 Apr 16, 2024
78850b8
chore: fix linter
huyhuynh3103 Apr 16, 2024
dc16164
chore: linter storagelayout.js
huyhuynh3103 Apr 16, 2024
3495978
feat: move ci into another files
huyhuynh3103 Apr 17, 2024
456898d
Merge pull request #10 from axieinfinity/feature/erc721-common-upgrad…
huyhuynh3103 Apr 17, 2024
a7e5379
chore: fix target
huyhuynh3103 Apr 19, 2024
84cc173
feat: solve convention issue and NAT spec
huyhuynh3103 Apr 19, 2024
fa34953
feat: include token id increament in _init_unchain
huyhuynh3103 Apr 19, 2024
29791ef
chore: optimize some gas
huyhuynh3103 Apr 19, 2024
f661705
chore: re-order the functions
huyhuynh3103 Apr 19, 2024
b659910
feat: add gap into ERC721CommonUpgradeable
huyhuynh3103 Apr 19, 2024
7173c6f
chore: explicit import
huyhuynh3103 Apr 19, 2024
dd1be7a
chore: add bracklet spacing
huyhuynh3103 Apr 19, 2024
3e44b81
chore: pump soldity version to 0.8.22
huyhuynh3103 Apr 19, 2024
b5916a3
chore: add more comments
huyhuynh3103 Apr 19, 2024
e4e69b5
chore: re-order the functions
huyhuynh3103 Apr 19, 2024
b44f7c0
chore: add more test support interfaces
huyhuynh3103 Apr 19, 2024
78b8307
chore: remove interface
huyhuynh3103 Apr 19, 2024
a00c009
fix: fix support interface for ERC721CommonUpgradeable contract
huyhuynh3103 Apr 19, 2024
8915eec
fix: fix support interface for ERC721Common
huyhuynh3103 Apr 19, 2024
35bf3b9
chore: fix comment
huyhuynh3103 Apr 19, 2024
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
2 changes: 2 additions & 0 deletions .github/template/create-pull-request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
### Description
PR to merge from [{{ .fromBranch }}](/axieinfinity/foundry-contract-template/tree/{{ .fromBranch }}) to [{{ .toBranch }}](/axieinfinity/foundry-contract-template/tree/{{ .toBranch }}).
54 changes: 54 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: test

on:
push:
branches:
- main
- dev
- 'feature/*'
- 'features/*'
pull_request:
branches:
- main
- dev
- 'feature/*'
- 'features/*'

env:
FOUNDRY_PROFILE: ci

jobs:
check:
strategy:
fail-fast: true

name: Foundry project
runs-on: [self-hosted, dockerize]
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: Run Forge build
run: |
forge --version
forge build --sizes
id: build

- name: Run Forge tests
run: |
forge test -vvv
id: test
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@
path = lib/openzeppelin-contracts
url = https://github.com/openzeppelin/openzeppelin-contracts
branch = v4.8.2
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
branch = v4.9.5
7 changes: 0 additions & 7 deletions .husky/pre-commit

This file was deleted.

7 changes: 5 additions & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ out = 'out'
libs = ['lib', 'party']

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
solc = '0.8.17'
evm_version = 'istanbul'
cache_path = 'cache/foundry'

extra_output = ["devdoc", "userdoc", "storagelayout"]
fs_permissions = [{ access = "read-write", path = "./" }]

[fmt]
line_length = 120
tab_width = 2
line_length = 120
bracket_spacing = true
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts-upgradeable
16 changes: 16 additions & 0 deletions scripts/deploy/logic/erc721-upgradeable-logic.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { HardhatRuntimeEnvironment } from 'hardhat/types';

const deploy = async ({ getNamedAccounts, deployments, ethers }: HardhatRuntimeEnvironment) => {
const { deploy } = deployments;
const { deployer } = await getNamedAccounts();
await deploy('SampleERC721CommonUpgradeableLogic', {
contract: 'ERC721CommonUpgradeable',
from: deployer,
log: true,
});
};

deploy.tags = ['SampleERC721CommonUpgradeableLogic'];
deploy.dependencies = ['VerifyContracts'];

export default deploy;
16 changes: 16 additions & 0 deletions scripts/deploy/proxy-admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { HardhatRuntimeEnvironment } from 'hardhat/types';

const deploy = async ({ getNamedAccounts, deployments, ethers }: HardhatRuntimeEnvironment) => {
const { deploy } = deployments;
const { deployer } = await getNamedAccounts();
await deploy('ProxyAdmin', {
contract: 'ProxyAdmin',
from: deployer,
log: true,
});
};

deploy.tags = ['ProxyAdmin'];
deploy.dependencies = ['VerifyContracts'];

export default deploy;
25 changes: 25 additions & 0 deletions scripts/deploy/proxy/erc721-upgradeable-proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ERC721CommonUpgradeable__factory } from '../../typechain-types';
import { HardhatRuntimeEnvironment } from 'hardhat/types';

const erc721Interface = ERC721CommonUpgradeable__factory.createInterface();

const deploy = async ({ getNamedAccounts, deployments, network }: HardhatRuntimeEnvironment) => {
const { deploy } = deployments;
const { deployer } = await getNamedAccounts();
const proxyAdmin = await deployments.get('ProxyAdmin');
const logicContract = await deployments.get('SampleERC721CommonUpgradeableLogic');

const data = erc721Interface.encodeFunctionData('initialize', ['SampleERC721', 'NFT', 'http://example.com/']);

await deploy('SampleERC721CommonUpgradeableProxy', {
contract: 'TransparentUpgradeableProxy',
from: deployer,
log: true,
args: [logicContract.address, proxyAdmin, data],
});
};

deploy.tags = ['SampleERC721CommonUpgradeableProxy'];
deploy.dependencies = ['VerifyContracts', 'ProxyAdmin', 'SampleERC721CommonUpgradeableLogic'];

export default deploy;
14 changes: 7 additions & 7 deletions src/ERC721Common.sol
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "./refs/IERC721State.sol";
import "./interfaces/IERC721State.sol";
import "./interfaces/IERC721Common.sol";
import "./refs/ERC721Nonce.sol";
import "./ERC721PresetMinterPauserAutoIdCustomized.sol";

abstract contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized, IERC721State {
abstract contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized, IERC721State, IERC721Common {
constructor(string memory name, string memory symbol, string memory baseTokenURI)
ERC721PresetMinterPauserAutoIdCustomized(name, symbol, baseTokenURI)
{}
{ }

/**
* @inheritdoc IERC721State
*/
/// @inheritdoc IERC721State
function stateOf(uint256 _tokenId) external view virtual override returns (bytes memory) {
require(_exists(_tokenId), "ERC721Common: query for non-existent token");
return abi.encodePacked(ownerOf(_tokenId), nonces[_tokenId], _tokenId);
Expand Down Expand Up @@ -41,7 +40,8 @@ abstract contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCus
override(ERC721, ERC721PresetMinterPauserAutoIdCustomized)
returns (bool)
{
return super.supportsInterface(interfaceId);
return interfaceId == type(IERC721Common).interfaceId || interfaceId == type(IERC721State).interfaceId
|| super.supportsInterface(interfaceId);
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/ERC721PresetMinterPauserAutoIdCustomized.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import "../lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Pa
import "../lib/openzeppelin-contracts/contracts/access/AccessControlEnumerable.sol";
import "../lib/openzeppelin-contracts/contracts/utils/Context.sol";
import "../lib/openzeppelin-contracts/contracts/utils/Counters.sol";
import "./interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol";

/**
* @dev ERC721PresetMinterPauserAutoId is a customized version of
Expand Down Expand Up @@ -118,7 +119,8 @@ contract ERC721PresetMinterPauserAutoIdCustomized is
override(AccessControlEnumerable, ERC721, ERC721Enumerable)
returns (bool)
{
return super.supportsInterface(interfaceId);
return
interfaceId == type(IERC721PresetMinterPauserAutoIdCustomized).interfaceId || super.supportsInterface(interfaceId);
}

/**
Expand Down
17 changes: 17 additions & 0 deletions src/interfaces/IERC721Common.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IERC721Common {
/**
* @dev Bulk create new tokens for `_recipients`. Tokens ID will be automatically
* assigned (and available on the emitted {IERC721Upgradeable-Transfer} event), and the token
* URI autogenerated based on the base URI passed at construction.
*
* See {ERC721Upgradeable-_mint}.
*
* Requirements:
*
* - the caller must have the `MINTER_ROLE`.
*/
function bulkMint(address[] calldata recipients) external returns (uint256[] memory tokenIds);
}
39 changes: 39 additions & 0 deletions src/interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IERC721PresetMinterPauserAutoIdCustomized {
/**
* @dev Creates a new token for `to`. Its token ID will be automatically
* assigned (and available on the emitted {IERC721Upgradeable-Transfer} event), and the token
* URI autogenerated based on the base URI passed at construction.
*
* See {ERC721Upgradeable-_mint}.
*
* Requirements:
*
* - the caller must have the `MINTER_ROLE`.
*/
function mint(address to) external returns (uint256 tokenId);

/**
* @dev Pauses all token transfers.
*
* See {ERC721PausableUpgradeable} and {PausableUpgradeable-_pause}.
*
* Requirements:
*
* - the caller must have the `PAUSER_ROLE`.
*/
function pause() external;

/**
* @dev Unpauses all token transfers.
*
* See {ERC721PausableUpgradeable} and {PausableUpgradeable-_unpause}.
*
* Requirements:
*
* - the caller must have the `PAUSER_ROLE`.
*/
function unpause() external;
}
File renamed without changes.
6 changes: 6 additions & 0 deletions src/mock/SampleERC721Upgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "../upgradeable/ERC721CommonUpgradeable.sol";

contract SampleERC721Upgradeable is ERC721CommonUpgradeable {}
91 changes: 91 additions & 0 deletions src/upgradeable/ERC721CommonUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { ERC721Upgradeable } from
"../../lib/openzeppelin-contracts-upgradeable/contracts/token/ERC721/ERC721Upgradeable.sol";
import { IERC721State } from "../interfaces/IERC721State.sol";
import { IERC721Common } from "../interfaces/IERC721Common.sol";
import { ERC721NonceUpgradeable } from "./refs/ERC721NonceUpgradeable.sol";
import { ERC721PresetMinterPauserAutoIdCustomizedUpgradeable } from
"./ERC721PresetMinterPauserAutoIdCustomizedUpgradeable.sol";

abstract contract ERC721CommonUpgradeable is
huyhuynh3103 marked this conversation as resolved.
Show resolved Hide resolved
ERC721NonceUpgradeable,
ERC721PresetMinterPauserAutoIdCustomizedUpgradeable,
IERC721State,
IERC721Common
{
error ErrInvalidArrayLength();
error ErrNonExistentToken();

/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
*/
uint256[50] private __gap;

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

/// @inheritdoc IERC721State
function stateOf(uint256 tokenId) external view virtual override returns (bytes memory) {
if (!_exists(tokenId)) revert ErrNonExistentToken();
return abi.encodePacked(ownerOf(tokenId), nonces(tokenId), tokenId);
}

/// @inheritdoc IERC721Common
function bulkMint(address[] calldata recipients)
external
virtual
onlyRole(MINTER_ROLE)
returns (uint256[] memory tokenIds)
{
uint256 length = recipients.length;
if (length == 0) revert ErrInvalidArrayLength();
tokenIds = new uint256[](length);

for (uint256 i; i < length; ++i) {
tokenIds[i] = _mintFor(recipients[i]);
}
}

/**
* @dev Override `IERC165-supportsInterface`.
*/
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC721Upgradeable, ERC721PresetMinterPauserAutoIdCustomizedUpgradeable)
returns (bool)
{
return interfaceId == type(IERC721State).interfaceId || interfaceId == type(IERC721Common).interfaceId
|| super.supportsInterface(interfaceId);
}

/**
* @dev Override `ERC721Upgradeable-_baseURI`.
*/
function _baseURI()
internal
view
virtual
override(ERC721Upgradeable, ERC721PresetMinterPauserAutoIdCustomizedUpgradeable)
returns (string memory)
{
return super._baseURI();
}

/**
* @dev Override `ERC721PresetMinterPauserAutoIdCustomizedUpgradeable-_beforeTokenTransfer`.
*/
function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize)
internal
virtual
override(ERC721NonceUpgradeable, ERC721PresetMinterPauserAutoIdCustomizedUpgradeable)
{
super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
}
}
Loading
Loading