Skip to content
Merged
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
28 changes: 28 additions & 0 deletions src/callback/UpdateTokenIdERC1155.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

contract UpdateTokenIdCallbackERC1155 {

/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/

error UpdateTokenIdCallbackERC1155NotImplemented();

/*//////////////////////////////////////////////////////////////
EXTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////*/

/**
* @notice The updateTokenIdERC1155 hook that is called by a core token before minting tokens.
*
* @dev If the tokenId is type(uint256).max, the next tokenId will be set to the current next tokenId + amount.
*
* @param _tokenId The tokenId to mint.
* @return result tokenId to mint.
*/
function updateTokenIdERC1155(uint256 _tokenId) external payable virtual returns (uint256) {
revert UpdateTokenIdCallbackERC1155NotImplemented();
}

}
39 changes: 30 additions & 9 deletions src/core/token/ERC1155Core.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {BeforeMintCallbackERC1155} from "../../callback/BeforeMintCallbackERC115
import {BeforeMintWithSignatureCallbackERC1155} from "../../callback/BeforeMintWithSignatureCallbackERC1155.sol";
import {BeforeTransferCallbackERC1155} from "../../callback/BeforeTransferCallbackERC1155.sol";
import {UpdateMetadataCallbackERC1155} from "../../callback/UpdateMetadataCallbackERC1155.sol";
import {UpdateTokenIdCallbackERC1155} from "../../callback/UpdateTokenIdERC1155.sol";

import {OnTokenURICallback} from "../../callback/OnTokenURICallback.sol";

Expand Down Expand Up @@ -142,7 +143,7 @@ contract ERC1155Core is ERC1155, Core, Multicallable, EIP712 {
override
returns (SupportedCallbackFunction[] memory supportedCallbackFunctions)
{
supportedCallbackFunctions = new SupportedCallbackFunction[](8);
supportedCallbackFunctions = new SupportedCallbackFunction[](9);
supportedCallbackFunctions[0] = SupportedCallbackFunction({
selector: BeforeMintCallbackERC1155.beforeMintERC1155.selector,
mode: CallbackMode.REQUIRED
Expand Down Expand Up @@ -173,6 +174,10 @@ contract ERC1155Core is ERC1155, Core, Multicallable, EIP712 {
selector: UpdateMetadataCallbackERC1155.updateMetadataERC1155.selector,
mode: CallbackMode.REQUIRED
});
supportedCallbackFunctions[8] = SupportedCallbackFunction({
selector: UpdateTokenIdCallbackERC1155.updateTokenIdERC1155.selector,
mode: CallbackMode.OPTIONAL
});
}

/*//////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -201,13 +206,14 @@ contract ERC1155Core is ERC1155, Core, Multicallable, EIP712 {
external
payable
{
uint256 tokenIdToMint = _updateTokenId(tokenId);
if (bytes(baseURI).length > 0) {
_updateMetadata(to, tokenId, amount, baseURI);
_updateMetadata(to, tokenIdToMint, amount, baseURI);
}
_beforeMint(to, tokenId, amount, data);
_beforeMint(to, tokenIdToMint, amount, data);

_totalSupply[tokenId] += amount;
_mint(to, tokenId, amount, "");
_totalSupply[tokenIdToMint] += amount;
_mint(to, tokenIdToMint, amount, "");
}

/**
Expand Down Expand Up @@ -236,13 +242,14 @@ contract ERC1155Core is ERC1155, Core, Multicallable, EIP712 {
)
).recover(signature);

uint256 tokenIdToMint = _updateTokenId(tokenId);
if (bytes(baseURI).length > 0) {
_updateMetadata(to, tokenId, amount, baseURI);
_updateMetadata(to, tokenIdToMint, amount, baseURI);
}
_beforeMintWithSignature(to, tokenId, amount, data, signer);
_beforeMintWithSignature(to, tokenIdToMint, amount, data, signer);

_totalSupply[tokenId] += amount;
_mint(to, tokenId, amount, "");
_totalSupply[tokenIdToMint] += amount;
_mint(to, tokenIdToMint, amount, "");
}

/**
Expand Down Expand Up @@ -393,6 +400,20 @@ contract ERC1155Core is ERC1155, Core, Multicallable, EIP712 {
);
}

/// @dev Calls the updateTokenId hook, if installed.
function _updateTokenId(uint256 tokenId) internal virtual returns (uint256 tokenIdToMint) {
(bool success, bytes memory returndata) = _executeCallbackFunction(
UpdateTokenIdCallbackERC1155.updateTokenIdERC1155.selector,
abi.encodeCall(UpdateTokenIdCallbackERC1155.updateTokenIdERC1155, (tokenId))
);
if (success) {
tokenIdToMint = abi.decode(returndata, (uint256));
} else {
// this will only occur when the callback is not implemented
tokenIdToMint = tokenId;
}
}

/// @dev Returns the domain name and version for EIP712.
function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) {
name = "ERC1155Core";
Expand Down
7 changes: 6 additions & 1 deletion src/core/token/ERC1155CoreInitializable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {BeforeMintCallbackERC1155} from "../../callback/BeforeMintCallbackERC115
import {BeforeMintWithSignatureCallbackERC1155} from "../../callback/BeforeMintWithSignatureCallbackERC1155.sol";
import {BeforeTransferCallbackERC1155} from "../../callback/BeforeTransferCallbackERC1155.sol";
import {UpdateMetadataCallbackERC1155} from "../../callback/UpdateMetadataCallbackERC1155.sol";
import {UpdateTokenIdCallbackERC1155} from "../../callback/UpdateTokenIdERC1155.sol";

import {OnTokenURICallback} from "../../callback/OnTokenURICallback.sol";

Expand Down Expand Up @@ -142,7 +143,7 @@ contract ERC1155CoreInitializable is ERC1155, Core, Multicallable, Initializable
override
returns (SupportedCallbackFunction[] memory supportedCallbackFunctions)
{
supportedCallbackFunctions = new SupportedCallbackFunction[](8);
supportedCallbackFunctions = new SupportedCallbackFunction[](9);
supportedCallbackFunctions[0] = SupportedCallbackFunction({
selector: BeforeMintCallbackERC1155.beforeMintERC1155.selector,
mode: CallbackMode.REQUIRED
Expand Down Expand Up @@ -173,6 +174,10 @@ contract ERC1155CoreInitializable is ERC1155, Core, Multicallable, Initializable
selector: UpdateMetadataCallbackERC1155.updateMetadataERC1155.selector,
mode: CallbackMode.REQUIRED
});
supportedCallbackFunctions[8] = SupportedCallbackFunction({
selector: UpdateTokenIdCallbackERC1155.updateTokenIdERC1155.selector,
mode: CallbackMode.OPTIONAL
});
}

/*//////////////////////////////////////////////////////////////
Expand Down
89 changes: 89 additions & 0 deletions src/module/token/tokenId/sequentialTokenIdERC1155.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {Module} from "../../../Module.sol";

import {Role} from "../../../Role.sol";

import {UpdateTokenIdCallbackERC1155} from "../../../callback/UpdateTokenIdERC1155.sol";
import {IInstallationCallback} from "../../../interface/IInstallationCallback.sol";

library SequentialTokenIdStorage {

/// @custom:storage-location erc7201:token.minting.tokenId
bytes32 public constant SEQUENTIAL_TOKEN_ID_STORAGE_POSITION =
keccak256(abi.encode(uint256(keccak256("token.tokenId.erc1155")) - 1)) & ~bytes32(uint256(0xff));

struct Data {
uint256 nextTokenId;
}

function data() internal pure returns (Data storage data_) {
bytes32 position = SEQUENTIAL_TOKEN_ID_STORAGE_POSITION;
assembly {
data_.slot := position
}
}

}

contract SequentialTokenIdERC1155 is Module, UpdateTokenIdCallbackERC1155 {

/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/

/// @dev Emitted when the tokenId is invalid.
error SequentialTokenIdInvalidTokenId();

/*//////////////////////////////////////////////////////////////
MODULE CONFIG
//////////////////////////////////////////////////////////////*/

/// @notice Returns all implemented callback and fallback functions.
function getModuleConfig() external pure override returns (ModuleConfig memory config) {
config.callbackFunctions = new CallbackFunction[](1);
config.fallbackFunctions = new FallbackFunction[](1);

config.callbackFunctions[0] = CallbackFunction(this.updateTokenIdERC1155.selector);

config.fallbackFunctions[0] = FallbackFunction({selector: this.getNextTokenId.selector, permissionBits: 0});

config.requiredInterfaces = new bytes4[](1);
config.requiredInterfaces[0] = 0xd9b67a26; // ERC1155
}

/*//////////////////////////////////////////////////////////////
CALLBACK FUNCTION
//////////////////////////////////////////////////////////////*/

function updateTokenIdERC1155(uint256 _tokenId) external payable override returns (uint256) {
uint256 _nextTokenId = _tokenIdStorage().nextTokenId;

if (_tokenId == type(uint256).max) {
_tokenIdStorage().nextTokenId = _nextTokenId + 1;

return _nextTokenId;
}

if (_tokenId > _nextTokenId) {
revert SequentialTokenIdInvalidTokenId();
}

return _tokenId;
}

/*//////////////////////////////////////////////////////////////
FALLBACK FUNCTIONS
//////////////////////////////////////////////////////////////*/

/// @notice Returns the sale configuration for a token.
function getNextTokenId() external view returns (uint256) {
return _tokenIdStorage().nextTokenId;
}

function _tokenIdStorage() internal pure returns (SequentialTokenIdStorage.Data storage) {
return SequentialTokenIdStorage.data();
}

}
3 changes: 1 addition & 2 deletions test/module/minting/ClaimableERC1155.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,8 @@ contract ClaimableERC1155Test is Test {
core.installModule(address(claimableModule), encodedInstallParams);

// install module
bytes memory encodedBatchMetadataInstallParams = "";
vm.prank(owner);
core.installModule(address(batchMetadataModule), encodedBatchMetadataInstallParams);
core.installModule(address(batchMetadataModule), "");

// Setup signature vars
typehashClaimSignatureParams =
Expand Down
3 changes: 1 addition & 2 deletions test/module/minting/MintableERC1155.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,8 @@ contract MintableERC1155Test is Test {
vm.prank(owner);
core.installModule(address(mintableModule), encodedInstallParams);

bytes memory encodedBatchMetadataInstallParams = "";
vm.prank(owner);
core.installModule(address(batchMetadataModule), encodedBatchMetadataInstallParams);
core.installModule(address(batchMetadataModule), "");

// Setup signature vars
typehashMintSignatureParams =
Expand Down
115 changes: 115 additions & 0 deletions test/module/tokenId/sequentialTokenIdERC1155.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "lib/forge-std/src/console.sol";

import {OwnableRoles} from "@solady/auth/OwnableRoles.sol";
import {ERC20} from "@solady/tokens/ERC20.sol";
import {Test} from "forge-std/Test.sol";

// Target contract

import {Core} from "src/Core.sol";
import {Module} from "src/Module.sol";

import {Role} from "src/Role.sol";
import {ERC1155Core} from "src/core/token/ERC1155Core.sol";

import {ICore} from "src/interface/ICore.sol";
import {IModuleConfig} from "src/interface/IModuleConfig.sol";

import {BatchMetadataERC1155} from "src/module/token/metadata/BatchMetadataERC1155.sol";
import {BatchMetadataERC721} from "src/module/token/metadata/BatchMetadataERC721.sol";
import {MintableERC1155} from "src/module/token/minting/MintableERC1155.sol";
import {SequentialTokenIdERC1155} from "src/module/token/tokenId/sequentialTokenIdERC1155.sol";

contract MockCurrency is ERC20 {

function mintTo(address _recipient, uint256 _amount) public {
_mint(_recipient, _amount);
}

/// @dev Returns the name of the token.
function name() public view virtual override returns (string memory) {
return "MockCurrency";
}

/// @dev Returns the symbol of the token.
function symbol() public view virtual override returns (string memory) {
return "MOCK";
}

}

contract MintableERC1155Test is Test {

ERC1155Core public core;

MintableERC1155 public mintableModule;
BatchMetadataERC1155 public batchMetadataModule;
SequentialTokenIdERC1155 public sequentialTokenIdModule;

uint256 ownerPrivateKey = 1;
address public owner;

address tokenRecipient = address(0x123);

MintableERC1155.MintSignatureParamsERC1155 public mintRequest;

// Constants
address private constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

function setUp() public {
owner = vm.addr(ownerPrivateKey);

address[] memory modules;
bytes[] memory moduleData;

core = new ERC1155Core("test", "TEST", "", owner, modules, moduleData);
mintableModule = new MintableERC1155();
batchMetadataModule = new BatchMetadataERC1155();
sequentialTokenIdModule = new SequentialTokenIdERC1155();

// install module
bytes memory encodedInstallParams = abi.encode(owner);
vm.prank(owner);
core.installModule(address(mintableModule), encodedInstallParams);

vm.prank(owner);
core.installModule(address(batchMetadataModule), "");

vm.prank(owner);
core.installModule(address(sequentialTokenIdModule), "");

// Give permissioned actor minter role
vm.prank(owner);
core.grantRoles(owner, Role._MINTER_ROLE);
}

/*//////////////////////////////////////////////////////////////
Tests: beforeMintERC1155
//////////////////////////////////////////////////////////////*/

function test_state_updateTokenId() public {
assertEq(SequentialTokenIdERC1155(address(core)).getNextTokenId(), 0);

// increments the tokenId
vm.prank(owner);
core.mint(owner, type(uint256).max, 10, "", "");

assertEq(SequentialTokenIdERC1155(address(core)).getNextTokenId(), 1);

// does not increment the tokenId
vm.prank(owner);
core.mint(owner, 1, 10, "", "");

assertEq(SequentialTokenIdERC1155(address(core)).getNextTokenId(), 1);
}

function test_revert_updateTokenId() public {
vm.expectRevert(SequentialTokenIdERC1155.SequentialTokenIdInvalidTokenId.selector);
vm.prank(owner);
core.mint(owner, 2, 1, "", "");
}

}
6 changes: 5 additions & 1 deletion test/module/transferable/TransferableERC1155.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ contract Core is ERC1155Core {
bytes[] memory moduleInstallData
) ERC1155Core(name, symbol, contractURI, owner, modules, moduleInstallData) {}

// disable mint and approve callbacks for these tests
// disable mint, approve and tokenId callbacks for these tests
function _beforeMint(address to, uint256 tokenId, uint256 value, bytes memory data) internal override {}
function _beforeApproveForAll(address from, address to, bool approved) internal override {}

function _updateTokenId(uint256 tokenId) internal override returns (uint256) {
return tokenId;
}

}

contract TransferableERC1155Test is Test {
Expand Down