Skip to content

Commit

Permalink
Adds single mint contract guards (#83)
Browse files Browse the repository at this point in the history
* fix tests

* refactor common code

* revert private

* add scripts, fix tests

* add 1155 single mint

* make it an admin mint guard

* fix mint method
  • Loading branch information
wwhchung authored Apr 9, 2024
1 parent 29653ba commit 11d70f9
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 2 deletions.
18 changes: 18 additions & 0 deletions packages/manifold/contracts/single/IManifoldERC1155Single.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

/**
* Manifold ERC1155 Single Mint interface
*/
interface IManifoldERC1155Single {

error InvalidInput();

/**
* @dev Mint a new token
*/
function mint(address creatorCore, uint256 expectedTokenId, string calldata uri, address[] calldata recipients, uint256[] calldata amounts) external;
}
18 changes: 18 additions & 0 deletions packages/manifold/contracts/single/IManifoldERC721Single.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

/**
* Manifold ERC721 Single Mint interface
*/
interface IManifoldERC721Single {

error InvalidInput();

/**
* @dev Mint a token
*/
function mint(address creatorCore, uint256 expectedTokenId, string calldata uri, address recipient) external;
}
35 changes: 35 additions & 0 deletions packages/manifold/contracts/single/ManifoldERC1155Single.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@manifoldxyz/libraries-solidity/contracts/access/IAdminControl.sol";
import "@manifoldxyz/creator-core-solidity/contracts/core/IERC1155CreatorCore.sol";
import "./IManifoldERC1155Single.sol";

/**
* Manifold ERC1155 Single Mint Implementation
*/
contract ManifoldERC1155Single is IManifoldERC1155Single {

/**
* @dev Only allows approved admins to call the specified function
*/
modifier creatorAdminRequired(address creator) {
if (!IAdminControl(creator).isAdmin(msg.sender)) revert("Must be owner or admin of creator contract");
_;
}

/**
* @dev See {IManifoldERC1155Single-mintNew}.
*/
function mint(address creatorCore, uint256 expectedTokenId, string calldata uri, address[] calldata recipients, uint256[] calldata amounts) external override creatorAdminRequired(creatorCore) {
string[] memory uris = new string[](1);
uris[0] = uri;
uint256[] memory tokenIds = IERC1155CreatorCore(creatorCore).mintBaseNew(recipients, amounts, uris);
if (tokenIds.length != 1 || tokenIds[0] != expectedTokenId) {
revert InvalidInput();
}
}
}
33 changes: 33 additions & 0 deletions packages/manifold/contracts/single/ManifoldERC721Single.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@manifoldxyz/libraries-solidity/contracts/access/IAdminControl.sol";
import "@manifoldxyz/creator-core-solidity/contracts/core/IERC721CreatorCore.sol";
import "./IManifoldERC721Single.sol";

/**
* Manifold ERC721 Single Mint Implementation
*/
contract ManifoldERC721Single is IManifoldERC721Single {

/**
* @dev Only allows approved admins to call the specified function
*/
modifier creatorAdminRequired(address creator) {
if (!IAdminControl(creator).isAdmin(msg.sender)) revert("Must be owner or admin of creator contract");
_;
}

/**
* @dev See {IManifoldERC721Single-mint}.
*/
function mint(address creatorCore, uint256 expectedTokenId, string calldata uri, address recipient) external override creatorAdminRequired(creatorCore) {
uint256 tokenId = IERC721CreatorCore(creatorCore).mintBase(recipient, uri);
if (tokenId != expectedTokenId) {
revert InvalidInput();
}
}
}
17 changes: 17 additions & 0 deletions packages/manifold/script/ManifoldERC1155Single.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import "../contracts/single/ManifoldERC1155Single.sol";

contract DeployManifoldERC1155Single is Script {
function run() external {
// uint256 deployerPrivateKey = pk; // uncomment this when testing on goerli
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); // comment this out when testing on goerli
vm.startBroadcast(deployerPrivateKey);
// forge script script/ManifoldERC1155Single.s.sol --optimizer-runs 1000 --rpc-url <YOUR_NODE> --broadcast
// forge verify-contract --compiler-version 0.8.17 --optimizer-runs 1000 --chain sepolia <DEPLOYED_ADDRESS> contracts/single/ManifoldERC1155Single.sol:ManifoldERC1155Single --watch
new ManifoldERC1155Single{salt: 0x4d616e69666f6c644552433131353553696e676c654d616e69666f6c64455243}();
vm.stopBroadcast();
}
}
4 changes: 2 additions & 2 deletions packages/manifold/script/ManifoldERC721Edition.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ contract DeployManifoldERC721Edition is Script {
// uint256 deployerPrivateKey = pk; // uncomment this when testing on goerli
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); // comment this out when testing on goerli
vm.startBroadcast(deployerPrivateKey);
// forge script scripts/ManifoldERC721Edition.s.sol --optimizer-runs 1000 --rpc-url <YOUR_NODE> --broadcast
// forge verify-contract --compiler-version 0.8.17 --optimizer-runs 1000 --chain sepolia <DEPLOYED_ADDRESS> contracts/edition/ManifoldERC721Edition.sol:ManifoldERC721Edition --constructor-args $(cast abi-encode "constructor(address)" "${INITIAL_OWNER}") --watch
// forge script script/ManifoldERC721Edition.s.sol --optimizer-runs 1000 --rpc-url <YOUR_NODE> --broadcast
// forge verify-contract --compiler-version 0.8.17 --optimizer-runs 1000 --chain sepolia <DEPLOYED_ADDRESS> contracts/edition/ManifoldERC721Edition.sol:ManifoldERC721Edition --watch
new ManifoldERC721Edition{salt: 0x4d616e69666f6c6445524337323145646974696f6e4d616e69666f6c64455243}();
vm.stopBroadcast();
}
Expand Down
17 changes: 17 additions & 0 deletions packages/manifold/script/ManifoldERC721Single.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import "../contracts/single/ManifoldERC721Single.sol";

contract DeployManifoldERC721Single is Script {
function run() external {
// uint256 deployerPrivateKey = pk; // uncomment this when testing on goerli
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); // comment this out when testing on goerli
vm.startBroadcast(deployerPrivateKey);
// forge script script/ManifoldERC721Single.s.sol --optimizer-runs 1000 --rpc-url <YOUR_NODE> --broadcast
// forge verify-contract --compiler-version 0.8.17 --optimizer-runs 1000 --chain sepolia <DEPLOYED_ADDRESS> contracts/single/ManifoldERC721Single.sol:ManifoldERC721Single --watch
new ManifoldERC721Single{salt: 0x4d616e69666f6c6445524337323153696e676c654d616e69666f6c6445524337}();
vm.stopBroadcast();
}
}
70 changes: 70 additions & 0 deletions packages/manifold/test/single/ManifoldERC1155Single.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Test.sol";
import "forge-std/console.sol";
import "../../contracts/single/ManifoldERC1155Single.sol";
import "@manifoldxyz/creator-core-solidity/contracts/ERC1155Creator.sol";

import "../mocks/Mock.sol";

contract ManifoldERC1155SingleTest is Test {
ManifoldERC1155Single public example;
ERC1155Creator public creatorCore;

address public owner = 0x6140F00e4Ff3936702E68744f2b5978885464cbB;
address public operator = 0xc78Dc443c126af6E4f6Ed540c1e740C1b5be09cd;

address public zeroAddress = address(0);
address public deadAddress = 0x000000000000000000000000000000000000dEaD;
uint256 private constant MAX_UINT_256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

function setUp() public {
vm.startPrank(owner);
creatorCore = new ERC1155Creator("Token", "NFT");

example = new ManifoldERC1155Single();

creatorCore.registerExtension(address(example), "");
vm.deal(owner, 10 ether);
vm.deal(operator, 10 ether);
vm.stopPrank();
}

function testAccess() public {
address[] memory recipients = new address[](1);
uint256[] memory amounts = new uint256[](1);
vm.startPrank(operator);
vm.expectRevert("Must be owner or admin of creator contract");
example.mint(address(creatorCore), 1, "", recipients, amounts);
vm.stopPrank();
}

function testMint() public {
vm.startPrank(owner);
creatorCore.approveAdmin(address(example));
address[] memory recipients = new address[](1);
uint256[] memory amounts = new uint256[](1);
recipients[0] = operator;
amounts[0] = 2;
example.mint(address(creatorCore), 1, "", recipients, amounts);
assertEq(creatorCore.balanceOf(operator, 1), 2);
// Can't mint same instance twice
vm.expectRevert(IManifoldERC1155Single.InvalidInput.selector);
example.mint(address(creatorCore), 1, "", recipients, amounts);
vm.stopPrank();
}

function testTokenURI() public {
vm.startPrank(owner);
creatorCore.approveAdmin(address(example));
address[] memory recipients = new address[](1);
uint256[] memory amounts = new uint256[](1);
recipients[0] = operator;
amounts[0] = 2;
example.mint(address(creatorCore), 1, "https://arweave.net/1hRadwN29sN5UDl_BBgH4RhCc2TjknMpuzGsP1t3wEM", recipients, amounts);
assertEq(creatorCore.uri(1), "https://arweave.net/1hRadwN29sN5UDl_BBgH4RhCc2TjknMpuzGsP1t3wEM");
vm.stopPrank();
}

}
62 changes: 62 additions & 0 deletions packages/manifold/test/single/ManifoldERC721Single.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Test.sol";
import "forge-std/console.sol";
import "../../contracts/single/ManifoldERC721Single.sol";
import "@manifoldxyz/creator-core-solidity/contracts/ERC721Creator.sol";

import "../mocks/Mock.sol";

contract ManifoldERC721SingleTest is Test {
ManifoldERC721Single public example;
ERC721Creator public creatorCore;

address public owner = 0x6140F00e4Ff3936702E68744f2b5978885464cbB;
address public operator = 0xc78Dc443c126af6E4f6Ed540c1e740C1b5be09cd;

address public zeroAddress = address(0);
address public deadAddress = 0x000000000000000000000000000000000000dEaD;
uint256 private constant MAX_UINT_256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;


function setUp() public {
vm.startPrank(owner);
creatorCore = new ERC721Creator("Token", "NFT");

example = new ManifoldERC721Single();

creatorCore.registerExtension(address(example), "");
vm.deal(owner, 10 ether);
vm.deal(operator, 10 ether);
vm.stopPrank();
}

function testAccess() public {
vm.startPrank(operator);
vm.expectRevert("Must be owner or admin of creator contract");
example.mint(address(creatorCore), 1, "", address(0));
vm.stopPrank();
}

function testMint() public {
vm.startPrank(owner);
creatorCore.approveAdmin(address(example));
example.mint(address(creatorCore), 1, "", operator);
assertEq(creatorCore.balanceOf(operator), 1);
// Can't mint same instance twice
vm.expectRevert(IManifoldERC721Single.InvalidInput.selector);
example.mint(address(creatorCore), 1, "", operator);
vm.stopPrank();
}

function testTokenURI() public {
vm.startPrank(owner);
creatorCore.approveAdmin(address(example));
example.mint(address(creatorCore), 1, "https://arweave.net/1hRadwN29sN5UDl_BBgH4RhCc2TjknMpuzGsP1t3wEM", operator);
assertEq(creatorCore.balanceOf(operator), 1);
assertEq(creatorCore.tokenURI(1), "https://arweave.net/1hRadwN29sN5UDl_BBgH4RhCc2TjknMpuzGsP1t3wEM");
vm.stopPrank();
}

}

0 comments on commit 11d70f9

Please sign in to comment.