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

Adds solady to comparison test #3

Open
wants to merge 5 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
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
[submodule "lib/ERC721A"]
path = lib/ERC721A
url = https://github.com/chiru-labs/ERC721A
[submodule "lib/solady"]
path = lib/solady
url = https://github.com/Vectorized/solady
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ of [ERC721A](https://github.com/chiru-labs/ERC721a) in Huff.


## Gas Comparison (ERC721H vs ERC721A vs OpenZeppelin's ERC721)
| Tests| Huff| Azuki|Azuki (delta vs. Huff)| OZ| OZ (delta vs. Huff)|
|---------------------------|--------|--------|-------------|----------|-----------|
| Mint 200| 437,232| 447,020| -9,788| 4,999,553| -4,562,321|
| Mint 50| 149,382| 152,120| -2,738| 1,273,403| -1,124,021|
| Safe Transfer To Receiver| 63,550| 65,436| -1,886| 66,160| -2,610|
| Simple Burn 1 In| 60,503| 82,611| -22,108| 38,893| +21,610|
| Simple Burn| 38,342| 60,214| -21,872| 38,893| -551|
|Simple Safe Transfer To EOA| 62,379| 63,364| -985| 64,151| -1,772|
| Simple Transfer 1 In| 81,987| 82,958| -971| 61,271| +20,716|
| Simple Transfer 20 In| 115,306| 119,835| -4,529| 61,271| +54,035|
| Simple Transfer| 59,817| 60,561| -744| 61,271| -1,454|
| Tests| Huff| Azuki|Azuki (Δ vs. Huff)| Solady| Solady (Δ vs. Huff)| OZ| OZ (Δ vs. Huff)|
|---------------------------|--------|--------|-------------|----------|-----------|----------|-----------|
| Mint 200| 437,232| 447,020| -9,788| 4,936,356| -4,499,124| 4,999,553| -4,562,321|
| Mint 50| 149,382| 152,120| -2,738| 1,257,606| -1,108,224| 1,273,403| -1,124,021|
| Safe Transfer To Receiver| 63,550| 65,436| -1,886| 62,347| +1,203| 66,160| -2,610|
| Simple Burn 1 In| 60,503| 82,611| -22,108| 36,976| +23,527| 38,893| +21,610|
| Simple Burn| 38,342| 60,214| -21,872| 36,976| +1,366| 38,893| -551|
|Simple Safe Transfer To EOA| 62,379| 63,364| -985| 60,675| +1,704| 64,151| -1,772|
| Simple Transfer 1 In| 81,987| 82,958| -971| 57,972| +24,015| 61,271| +20,716|
| Simple Transfer 20 In| 115,306| 119,835| -4,529| 57,972| +57,334| 61,271| +54,035|
| Simple Transfer| 59,817| 60,561| -744| 57,972| +1,845| 61,271| -1,454|

### Gas Comparison - Methodology
**General Approach:**
Expand Down
1 change: 1 addition & 0 deletions lib/solady
Submodule solady added at a549da
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
@openzeppelin/=lib/openzeppelin-contracts/
erc721a/=lib/ERC721A/
solady/=lib/solady/
7 changes: 6 additions & 1 deletion script/extract_gas_cost_comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ def main():
lines = clean_str_arr(raw_gas_data.splitlines())[3:-1]
tests = [[]]
for row in lines:
if row.startswith("Running"):
continue
if row == '':
tests.append([])
else:
Expand All @@ -61,6 +63,8 @@ def main():
'Huff': [],
'OZ': [],
'OZ (delta)': [],
'Solady': [],
'Solady (delta)': [],
'Azuki': [],
'Azuki (delta)': []
}
Expand All @@ -70,8 +74,9 @@ def main():
for contract_type, gas in individual_tests:
contract_types[contract_type].append(gas)

for huff, oz, azuki in zip(contract_types['Huff'], contract_types['OZ'], contract_types['Azuki']):
for huff, oz, solady, azuki in zip(contract_types['Huff'], contract_types['OZ'], contract_types['Solady'], contract_types['Azuki']):
contract_types['OZ (delta)'].append(huff - oz)
contract_types['Solady (delta)'].append(huff - solady)
contract_types['Azuki (delta)'].append(huff - azuki)

for col, col_values in contract_types.items():
Expand Down
10 changes: 8 additions & 2 deletions src/ERC721H.huff
Original file line number Diff line number Diff line change
Expand Up @@ -354,11 +354,17 @@
// takes: [to, quantity]

// --- check quantity > 0
__FUNC_SIG(MintZeroQuantity) dup3 REQUIRE(<zero>) pop
dup2 quantity_gt_zero jumpi
__FUNC_SIG(MintZeroQuantity) REVERT_SIG(<zero>)
quantity_gt_zero:

// [to, quantity]

// --- check address != 0
__FUNC_SIG(MintToZeroAddress) dup2 REQUIRE(<zero>) pop
dup1 address_not_zero jumpi
__FUNC_SIG(MintToZeroAddress) REVERT_SIG(<zero>)
address_not_zero:

// [to, quantity]

// --- update balance
Expand Down
45 changes: 45 additions & 0 deletions src/refs/ERC721Solady.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import {ERC721} from "solady/src/tokens/ERC721.sol";

contract ERC721Solady is ERC721 {
uint256 public constant START_TOKEN_ID = 1;

uint256 public totalSupply;

function mint(address _to, uint256 _quantity) external {
unchecked {
uint256 initialTokenId = totalSupply + START_TOKEN_ID;
for (uint256 i; i < _quantity; i++) {
_mint(_to, initialTokenId + i);
}
totalSupply += _quantity;
}
}

function safeMint(address _to, uint256 _quantity) external {
unchecked {
uint256 initialTokenId = totalSupply + START_TOKEN_ID;
for (uint256 i; i < _quantity; i++) {
_safeMint(_to, initialTokenId + i);
}
totalSupply += _quantity;
}
}

function burn(uint256 _tokenId) external {
_burn(_tokenId);
unchecked {
totalSupply--;
}
}

function name() public view virtual override returns (string memory) {}

/// @dev Returns the token collection symbol.
function symbol() public view virtual override returns (string memory) {}

/// @dev Returns the Uniform Resource Identifier (URI) for token `id`.
function tokenURI(uint256 id) public view virtual override returns (string memory) {}
}
48 changes: 48 additions & 0 deletions test/CompareERC721.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {HuffDeployer} from "foundry-huff/HuffDeployer.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {IMockERC721H} from "../src/refs/IMockERC721H.sol";
import {ERC721Azuki} from "../src/refs/ERC721Azuki.sol";
import {ERC721Solady} from "../src/refs/ERC721Solady.sol";
import {ERC721OZ} from "../src/refs/ERC721OZ.sol";

contract ERC721Receiver is ERC721Holder {}
Expand All @@ -14,6 +15,7 @@ contract ERC721Receiver is ERC721Holder {}
contract CompareERC721Test is Test {
IMockERC721H internal erc721h;
ERC721Azuki internal erc721a;
ERC721Solady internal erc721s;
ERC721OZ internal erc721oz;
address internal receiver;

Expand All @@ -34,6 +36,11 @@ contract CompareERC721Test is Test {
vm.prank(USER1);
erc721a.transferFrom(USER1, USER1, 1);

erc721s = new ERC721Solady();
erc721s.mint(USER1, 20);
vm.prank(USER1);
erc721s.transferFrom(USER1, USER1, 1);

erc721oz = new ERC721OZ();
erc721oz.mint(USER1, 20);
vm.prank(USER1);
Expand All @@ -46,6 +53,10 @@ contract CompareERC721Test is Test {
erc721oz.mint(USER1, 50);
}

function testMint50Solady() public {
erc721s.mint(USER1, 50);
}

function testMint50Azuki() public {
erc721a.mint(USER1, 50);
}
Expand All @@ -58,6 +69,10 @@ contract CompareERC721Test is Test {
erc721oz.mint(USER1, 200);
}

function testMint200Solady() public {
erc721s.mint(USER1, 200);
}

function testMint200Azuki() public {
erc721a.mint(USER1, 200);
}
Expand All @@ -71,6 +86,11 @@ contract CompareERC721Test is Test {
erc721oz.transferFrom(USER1, USER2, 20);
}

function testSimpleTransfer20InSolady() public {
vm.prank(USER1);
erc721s.transferFrom(USER1, USER2, 20);
}

function testSimpleTransfer20InAzuki() public {
vm.prank(USER1);
erc721a.transferFrom(USER1, USER2, 20);
Expand All @@ -86,6 +106,11 @@ contract CompareERC721Test is Test {
erc721oz.transferFrom(USER1, USER2, 2);
}

function testSimpleTransfer1InSolady() public {
vm.prank(USER1);
erc721s.transferFrom(USER1, USER2, 2);
}

function testSimpleTransfer1InAzuki() public {
vm.prank(USER1);
erc721a.transferFrom(USER1, USER2, 2);
Expand All @@ -101,6 +126,11 @@ contract CompareERC721Test is Test {
erc721oz.transferFrom(USER1, USER2, 1);
}

function testSimpleTransferSolady() public {
vm.prank(USER1);
erc721s.transferFrom(USER1, USER2, 1);
}

function testSimpleTransferAzuki() public {
vm.prank(USER1);
erc721a.transferFrom(USER1, USER2, 1);
Expand All @@ -115,6 +145,10 @@ contract CompareERC721Test is Test {
erc721oz.burn(1);
}

function testSimpleBurnSolady() public {
erc721s.burn(1);
}

function testSimpleBurnAzuki() public {
erc721a.burn(1);
}
Expand All @@ -127,6 +161,10 @@ contract CompareERC721Test is Test {
erc721oz.burn(2);
}

function testSimpleBurn1InSolady() public {
erc721s.burn(2);
}

function testSimpleBurn1InAzuki() public {
erc721a.burn(2);
}
Expand All @@ -140,6 +178,11 @@ contract CompareERC721Test is Test {
erc721oz.safeTransferFrom(USER1, USER2, 1);
}

function testSimpleSafeTransferToEoaSolady() public {
vm.prank(USER1);
erc721s.safeTransferFrom(USER1, USER2, 1);
}

function testSimpleSafeTransferToEoaAzuki() public {
vm.prank(USER1);
erc721a.safeTransferFrom(USER1, USER2, 1);
Expand All @@ -155,6 +198,11 @@ contract CompareERC721Test is Test {
erc721oz.safeTransferFrom(USER1, receiver, 1, hex"010203");
}

function testSafeTransferToReceiverSolady() public {
vm.prank(USER1);
erc721s.safeTransferFrom(USER1, receiver, 1, hex"010203");
}

function testSafeTransferToReceiverAzuki() public {
vm.prank(USER1);
erc721a.safeTransferFrom(USER1, receiver, 1, hex"010203");
Expand Down