Skip to content

Commit

Permalink
T
Browse files Browse the repository at this point in the history
  • Loading branch information
Vectorized committed Feb 10, 2024
1 parent ef4bfe1 commit 14704e0
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 23 deletions.
15 changes: 8 additions & 7 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
DN404Test:testBurnOnTransfer(uint32,address,address) (runs: 256, μ: 257735, ~: 259368)
DN404Test:testInitialize(uint32,address) (runs: 256, μ: 95178, ~: 108931)
DN404Test:testMintOnTransfer(uint32,address,address) (runs: 256, μ: 213209, ~: 214764)
DN404Test:testNameAndSymbol(string,string) (runs: 256, μ: 206354, ~: 206695)
DN404Test:testRegisterAndResolveAlias(address,address) (runs: 256, μ: 120599, ~: 120599)
DN404Test:testTokenURI(string,uint256) (runs: 256, μ: 162148, ~: 146462)
DN404Test:test__codesize() (gas: 17853)
DN404Test:testBurnOnTransfer(uint32,address,address) (runs: 256, μ: 277323, ~: 279111)
DN404Test:testInitialize(uint32,address) (runs: 256, μ: 97313, ~: 112344)
DN404Test:testMintOnTransfer(uint32,address,address) (runs: 256, μ: 254835, ~: 256235)
DN404Test:testNameAndSymbol(string,string) (runs: 256, μ: 206378, ~: 206719)
DN404Test:testRegisterAndResolveAlias(address,address) (runs: 256, μ: 120543, ~: 120621)
DN404Test:testSetAndGetOperatorApprovals(address,address,bool) (runs: 256, μ: 129145, ~: 120210)
DN404Test:testTokenURI(string,uint256) (runs: 256, μ: 157275, ~: 134976)
DN404Test:test__codesize() (gas: 20274)
SoladyTest:test__codesize() (gas: 1075)
TestPlus:test__codesize() (gas: 398)
45 changes: 40 additions & 5 deletions src/DN404.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ abstract contract DN404 {

error TransferToNonERC721ReceiverImplementer();

error TokenDoesNotExist();

error CannotLink();

error AlreadyLinked();

error NotLinked();

uint256 private constant _WAD = 1000000000000000000;

uint256 private constant _MAX_TOKEN_ID = 0xffffffff;
Expand Down Expand Up @@ -121,11 +129,6 @@ abstract contract DN404 {
return _getDN404Storage().addressData[owner].balance;
}

function ownerOf(uint256 id) public view virtual returns (address owner) {
DN404Storage storage $ = _getDN404Storage();
owner = $.aliasToAddress[$.ownerships.get(id)];
}

function approve(address spender, uint256 amount) public virtual returns (bool) {
DN404Storage storage $ = _getDN404Storage();

Expand Down Expand Up @@ -360,6 +363,27 @@ abstract contract DN404 {

uint256 fnSelector = _calldataload(0x00) >> 224;

// `isApprovedForAll(address,address)`.
if (fnSelector == 0xe985e9c5) {
if (msg.sender != $.sisterERC721) revert Unauthorized();
if (msg.data.length < 0x44) revert();

address owner = address(uint160(_calldataload(0x04)));
address operator = address(uint160(_calldataload(0x24)));

_return($.operatorApprovals[owner][operator] ? 1 : 0);
}
// `ownerOf(uint256)`.
if (fnSelector == 0x6352211e) {
if (msg.sender != $.sisterERC721) revert Unauthorized();
if (msg.data.length < 0x24) revert();

uint256 id = _calldataload(0x04);
address owner = $.aliasToAddress[$.ownerships.get(id)];
if (owner == address(0)) revert TokenDoesNotExist();

_return(uint160(owner));
}
// `_transferFromNFT(address,address,uint256,address)`.
if (fnSelector == 0xe5eb36c8) {
if (msg.sender != $.sisterERC721) revert Unauthorized();
Expand Down Expand Up @@ -396,6 +420,17 @@ abstract contract DN404 {

_return(uint160(_approveNFT(spender, id, msgSender)));
}
// `getApproved(uint256)`.
if (fnSelector == 0x081812fc) {
if (msg.sender != $.sisterERC721) revert Unauthorized();
if (msg.data.length < 0x24) revert();

uint256 id = _calldataload(0x04);
address owner = $.aliasToAddress[$.ownerships.get(id)];
if (owner == address(0)) revert TokenDoesNotExist();

_return(uint160($.tokenApprovals[id]));
}
// `implementsDN404()`.
if (fnSelector == 0xb7a94eb8) {
_return(1);
Expand Down
69 changes: 63 additions & 6 deletions src/DN404NonFungibleShadow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,26 @@ contract DN404NonFungibleShadow {
/* CUSTOM ERRORS */
/*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

error AlreadyInitialized();

error InvalidTotalNFTSupply();

error FailToLinkToSister();

error Unauthorized();

error TransferToZeroAddress();

error SisterAddressIsZero();

error ApprovalCallerNotOwnerNorApproved();

error TransferCallerNotOwnerNorApproved();

error TransferFromIncorrectOwner();

error TransferToNonERC721ReceiverImplementer();

error TokenDoesNotExist();

error CannotLink();
Expand All @@ -26,8 +44,6 @@ contract DN404NonFungibleShadow {

error NotLinked();

error TransferToNonERC721ReceiverImplementer();

uint256 private constant _WAD = 1000000000000000000;

struct DN404NFTStorage {
Expand Down Expand Up @@ -97,7 +113,7 @@ contract DN404NonFungibleShadow {
address sister = sisterERC20();
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x7824407f) // `tokenSupply()`.
mstore(0x00, 0x18160ddd) // `totalSupply()`.
if iszero(
and(gt(returndatasize(), 0x1f), staticcall(gas(), sister, 0x1c, 0x04, 0x00, 0x20))
) {
Expand All @@ -124,7 +140,7 @@ contract DN404NonFungibleShadow {
}
}

function ownerOf(uint256 id) public view virtual returns (address owner) {
function ownerOf(uint256 id) public view virtual returns (address result) {
address sister = sisterERC20();
/// @solidity memory-safe-assembly
assembly {
Expand All @@ -136,9 +152,8 @@ contract DN404NonFungibleShadow {
returndatacopy(mload(0x40), 0x00, returndatasize())
revert(mload(0x40), returndatasize())
}
owner := shr(96, shl(96, mload(0x00)))
result := shr(96, shl(96, mload(0x00)))
}
if (owner == address(0)) revert TokenDoesNotExist();
}

function approve(address spender, uint256 id) public virtual {
Expand All @@ -165,6 +180,22 @@ contract DN404NonFungibleShadow {
emit Approval(owner, spender, id);
}

function getApproved(uint256 id) public view virtual returns (address result) {
address sister = sisterERC20();
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x081812fc) // `getApproved(uint256)`.
mstore(0x20, id)
if iszero(
and(gt(returndatasize(), 0x1f), staticcall(gas(), sister, 0x1c, 0x24, 0x00, 0x20))
) {
returndatacopy(mload(0x40), 0x00, returndatasize())
revert(mload(0x40), returndatasize())
}
result := shr(96, shl(96, mload(0x00)))
}
}

function setApprovalForAll(address operator, bool approved) public virtual {
address sister = sisterERC20();
/// @solidity memory-safe-assembly
Expand All @@ -187,6 +218,32 @@ contract DN404NonFungibleShadow {
emit ApprovalForAll(msg.sender, operator, approved);
}

function isApprovedForAll(address owner, address operator)
public
view
virtual
returns (bool result)
{
address sister = sisterERC20();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, 0xe985e9c5) // `isApprovedForAll(address,address)`.
mstore(add(m, 0x20), shr(96, shl(96, owner)))
mstore(add(m, 0x40), shr(96, shl(96, operator)))
if iszero(
and(
gt(returndatasize(), 0x1f),
staticcall(gas(), sister, add(m, 0x1c), 0x44, 0x00, 0x20)
)
) {
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
result := iszero(iszero(mload(0x00)))
}
}

function transferFrom(address from, address to, uint256 id) public virtual {
address sister = sisterERC20();
/// @solidity memory-safe-assembly
Expand Down
28 changes: 25 additions & 3 deletions test/DN404.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ pragma solidity ^0.8.24;
import "./utils/SoladyTest.sol";
import {DN404, MockDN404} from "./utils/mocks/MockDN404.sol";
import {DN404NonFungibleShadow} from "../src/DN404NonFungibleShadow.sol";
import "solady/utils/LibString.sol";

contract DN404Test is SoladyTest {
uint256 private constant _WAD = 1000000000000000000;

MockDN404 dn;
DN404NonFungibleShadow shadow;

Expand All @@ -25,7 +26,7 @@ contract DN404Test is SoladyTest {
function testTokenURI(string memory baseURI, uint256 id) public {
dn.initializeDN404(1000, address(this), address(shadow));
dn.setBaseURI(baseURI);
assertEq(shadow.tokenURI(id), string(abi.encodePacked(baseURI, LibString.toString(id))));
assertEq(shadow.tokenURI(id), string(abi.encodePacked(baseURI, id)));
}

function testRegisterAndResolveAlias(address a0, address a1) public {
Expand All @@ -49,9 +50,21 @@ contract DN404Test is SoladyTest {
dn.initializeDN404(totalNFTSupply, initialSupplyOwner, address(shadow));
assertEq(dn.totalSupply(), uint256(totalNFTSupply) * 10 ** 18);
assertEq(dn.balanceOf(initialSupplyOwner), uint256(totalNFTSupply) * 10 ** 18);
assertEq(shadow.totalSupply(), totalNFTSupply);
assertEq(shadow.balanceOf(initialSupplyOwner), totalNFTSupply);
}
}

function testSetAndGetOperatorApprovals(address owner, address operator, bool approved)
public
{
dn.initializeDN404(1000, address(this), address(shadow));
assertEq(shadow.isApprovedForAll(owner, operator), false);
vm.prank(owner);
shadow.setApprovalForAll(operator, approved);
assertEq(shadow.isApprovedForAll(owner, operator), approved);
}

function testMintOnTransfer(
uint32 totalNFTSupply,
address initialSupplyOwner,
Expand All @@ -65,10 +78,19 @@ contract DN404Test is SoladyTest {

dn.initializeDN404(totalNFTSupply, initialSupplyOwner, address(shadow));

vm.expectRevert(DN404NonFungibleShadow.TokenDoesNotExist.selector);
shadow.getApproved(1);

vm.prank(initialSupplyOwner);
dn.transfer(recipient, 1e18);
dn.transfer(recipient, _WAD);

assertEq(shadow.balanceOf(recipient), 1);
assertEq(shadow.ownerOf(1), recipient);

assertEq(shadow.getApproved(1), address(0));
vm.prank(recipient);
shadow.approve(address(this), 1);
assertEq(shadow.getApproved(1), address(this));
}

function testBurnOnTransfer(
Expand Down
3 changes: 1 addition & 2 deletions test/utils/mocks/MockDN404.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
pragma solidity ^0.8.4;

import "../../../src/DN404.sol";
import "solady/utils/LibString.sol";

contract MockDN404 is DN404 {
string private _name;
Expand All @@ -29,7 +28,7 @@ contract MockDN404 is DN404 {
}

function tokenURI(uint256 id) public view virtual override returns (string memory) {
return string(abi.encodePacked(_baseURI, LibString.toString(id)));
return string(abi.encodePacked(_baseURI, id));
}

function setWhitelist(address target, bool status) public {
Expand Down

0 comments on commit 14704e0

Please sign in to comment.