Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
bingen committed Feb 28, 2024
0 parents commit c516b9a
Show file tree
Hide file tree
Showing 15 changed files with 281 additions and 0 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: test

on: workflow_dispatch

env:
FOUNDRY_PROFILE: ci

jobs:
check:
strategy:
fail-fast: true

name: Foundry project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- 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
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Compiler files
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/

# Docs
docs/

# Dotenv file
.env
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/openzeppelin/openzeppelin-contracts
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# NFT gas comparison

![Screenshot_20240228_122650](https://github.com/liquity/nft_gas_comparison/assets/701095/ab25af18-9d1b-4162-aa43-d95a141e626b)

![Screenshot_20240228_122726](https://github.com/liquity/nft_gas_comparison/assets/701095/34f88616-6cb2-481d-80a9-08ecd80da59d)
6 changes: 6 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at ae570f
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at 01ef44
12 changes: 12 additions & 0 deletions script/Counter.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Script, console2} from "forge-std/Script.sol";

contract CounterScript is Script {
function setUp() public {}

function run() public {
vm.broadcast();
}
}
106 changes: 106 additions & 0 deletions src/Dependencies/ERC721SemiEnumerable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.20;

import {ERC721} from "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol";
import {IERC165} from "openzeppelin-contracts/contracts/utils/introspection/ERC165.sol";

/**
* @dev This implements an optional extension of {ERC721} defined in the EIP that adds enumerability
* of all the token ids in the contract as well as all token ids owned by each account.
*
* CAUTION: `ERC721` extensions that implement custom `balanceOf` logic, such as `ERC721Consecutive`,
* interfere with enumerability and should not be used together with `ERC721Enumerable`.
*/
abstract contract ERC721SemiEnumerable is ERC721 {
mapping(address owner => mapping(uint256 index => uint256)) private _ownedTokens;
mapping(uint256 tokenId => uint256) private _ownedTokensIndex;

/**
* @dev An `owner`'s token query was out of bounds for `index`.
*
* NOTE: The owner being `address(0)` indicates a global out of bounds index.
*/
error ERC721OutOfBoundsIndex(address owner, uint256 index);

/**
* @dev Batch mint is not allowed.
*/
error ERC721EnumerableForbiddenBatchMint();

/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual returns (uint256) {
if (index >= balanceOf(owner)) {
revert ERC721OutOfBoundsIndex(owner, index);
}
return _ownedTokens[owner][index];
}

/**
* @dev See {ERC721Enumerable-_update}.
*/
function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
address previousOwner = super._update(to, tokenId, auth);

if (previousOwner != to ) {
if (previousOwner != address(0)) {
_removeTokenFromOwnerEnumeration(previousOwner, tokenId);
}
_addTokenToOwnerEnumeration(to, tokenId);
}

return previousOwner;
}

/**
* @dev Private function to add a token to this extension's ownership-tracking data structures.
* @param to address representing the new owner of the given token ID
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
*/
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
uint256 length = balanceOf(to) - 1;
_ownedTokens[to][length] = tokenId;
_ownedTokensIndex[tokenId] = length;
}

/**
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
* while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
* gas optimizations e.g. when performing a transfer operation (avoiding double writes).
* This has O(1) time complexity, but alters the order of the _ownedTokens array.
* @param from address representing the previous owner of the given token ID
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).

uint256 lastTokenIndex = balanceOf(from);
uint256 tokenIndex = _ownedTokensIndex[tokenId];

// When the token to delete is the last token, the swap operation is unnecessary
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
}

// This also deletes the contents at the last position of the array
delete _ownedTokensIndex[tokenId];
delete _ownedTokens[from][lastTokenIndex];
}

/**
* See {ERC721-_increaseBalance}. We need that to account tokens that were minted in batch
*/
function _increaseBalance(address account, uint128 amount) internal virtual override {
if (amount > 0) {
revert ERC721EnumerableForbiddenBatchMint();
}
super._increaseBalance(account, amount);
}
}
13 changes: 13 additions & 0 deletions src/NFTCustom.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import "./Dependencies/ERC721SemiEnumerable.sol";


contract NFTCustom is ERC721SemiEnumerable {
constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) {}

function mint(uint256 _tokenId) external {
_mint(msg.sender, _tokenId);
}
}
13 changes: 13 additions & 0 deletions src/NFTEnumerable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import "openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Enumerable.sol";


contract NFTEnumerable is ERC721Enumerable {
constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) {}

function mint(uint256 _tokenId) external {
_mint(msg.sender, _tokenId);
}
}
13 changes: 13 additions & 0 deletions src/NFTRaw.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol";


contract NFTRaw is ERC721 {
constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) {}

function mint(uint256 _tokenId) external {
_mint(msg.sender, _tokenId);
}
}
19 changes: 19 additions & 0 deletions test/NFTCustom.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console2} from "forge-std/Test.sol";
import {NFTCustom} from "../src/NFTCustom.sol";

contract NFTCustomTest is Test {
NFTCustom public nft;

function setUp() public {
nft = new NFTCustom("NFTCustom", "NFTC");
}

function test_mint() public {
for(uint256 i = 0; i < 10; i++) {
nft.mint(i);
}
}
}
19 changes: 19 additions & 0 deletions test/NFTEnumerable.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console2} from "forge-std/Test.sol";
import {NFTEnumerable} from "../src/NFTEnumerable.sol";

contract NFTEnumerableTest is Test {
NFTEnumerable public nft;

function setUp() public {
nft = new NFTEnumerable("NFTEnumerable", "NFTE");
}

function test_mint() public {
for(uint256 i = 0; i < 10; i++) {
nft.mint(i);
}
}
}
19 changes: 19 additions & 0 deletions test/NFTRaw.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console2} from "forge-std/Test.sol";
import {NFTRaw} from "../src/NFTRaw.sol";

contract NFTRawTest is Test {
NFTRaw public nft;

function setUp() public {
nft = new NFTRaw("NFTRaw", "NFTR");
}

function test_mint() public {
for(uint256 i = 0; i < 10; i++) {
nft.mint(i);
}
}
}

0 comments on commit c516b9a

Please sign in to comment.