Skip to content

Commit

Permalink
add basic Product & ProductFactory
Browse files Browse the repository at this point in the history
  • Loading branch information
Kabie committed Jul 4, 2024
1 parent 65a3d02 commit 8164602
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 55 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ docs/
.env

/dependencies
/.vscode
3 changes: 2 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ test = "test"
libs = ["dependencies"]

[dependencies]
forge-std = { version = "1.9.1"}
"@openzeppelin-contracts" = { version = "5.0.2" }
forge-std = { version = "1.8.2"}
"@openzeppelin-contracts-upgradeable" = { version = "5.0.2" }

3 changes: 2 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
forge-std=dependencies/forge-std-1.8.2/src
forge-std=dependencies/forge-std-1.9.1/src
@openzeppelin/contracts=dependencies/@openzeppelin-contracts-5.0.2
@openzeppelin/contracts-upgradeable=dependencies/@openzeppelin-contracts-upgradeable-5.0.2
12 changes: 0 additions & 12 deletions script/Counter.s.sol

This file was deleted.

12 changes: 9 additions & 3 deletions soldeer.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ version = "5.0.2"
source = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/5_0_2_14-03-2024_06:11:59_contracts.zip"
checksum = "8bc4f0acc7c187771b878d46f7de4bfad1acad2eb5d096d9d05d34035853f5c3"

[[dependencies]]
name = "@openzeppelin-contracts-upgradeable"
version = "5.0.2"
source = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts-upgradeable/5_0_2_14-03-2024_06:12:07_contracts-upgradeable.zip"
checksum = "fb3f8db8541fc01636f91b0e7d9dd6f450f1bf7e2b4a17e96caf6e779ace8f5b"

[[dependencies]]
name = "forge-std"
version = "1.8.2"
source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/1_8_2_19-05-2024_18:52:07_forge-std-1.8.2.zip"
checksum = "88a37e1d79f60b8aad08c7bd50a7a5ef973fc172b1495028d0725a17f5a4976c"
version = "1.9.1"
source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/v1_9_1_03-07-2024_14:44:59_forge-std-v1.9.1.zip"
checksum = "110b35ad3604d91a919c521c71206c18cd07b29c750bd90b5cbbaf37288c9636"
14 changes: 0 additions & 14 deletions src/Counter.sol

This file was deleted.

35 changes: 35 additions & 0 deletions src/Product.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract Product is Initializable, ERC721Upgradeable, OwnableUpgradeable {
string public baseURI;

constructor() {
_disableInitializers();
}

function initialize(address initialOwner, string calldata name, string calldata symbol) public initializer {
__ERC721_init(name, symbol);
__Ownable_init(initialOwner);
}

function setBaseURI(string calldata baseURI_) external onlyOwner {
baseURI = baseURI_;
}

function _baseURI() internal view override returns (string memory) {
return baseURI;
}

function mint(address to, uint256 tokenId) public onlyOwner {
_mint(to, tokenId);
}

function safeMint(address to, uint256 tokenId) public onlyOwner {
_safeMint(to, tokenId);
}
}
38 changes: 38 additions & 0 deletions src/ProductFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Product} from "./Product.sol";

contract ProductFactory is Ownable {
address public product_template;

constructor(address product_template_) Ownable(msg.sender) {
setProgramTemplate(product_template_);
}

function setProgramTemplate(address product_template_) public onlyOwner {
requireIsContract(product_template_);
product_template = product_template_;
}

function requireIsContract(address contract_address) internal view {
uint256 code_size;
assembly {
code_size := extcodesize(contract_address)
}
require(code_size > 0);
}

function createProduct(string calldata name, string calldata symbol) external returns (Product product) {
address product_address = _createProduct(product_template, msg.sender, name);
product = Product(product_address);
product.initialize(msg.sender, name, symbol);
}

function _createProduct(address implementation, address vendor, string calldata name) internal returns (address) {
bytes32 salt = keccak256(abi.encodePacked("DePHY_ID", vendor, name));
return Clones.cloneDeterministic(implementation, salt);
}
}
24 changes: 0 additions & 24 deletions test/Counter.t.sol

This file was deleted.

64 changes: 64 additions & 0 deletions test/ProductFactory.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {Test, console} from "forge-std/Test.sol";
import {Product} from "../src/Product.sol";
import {ProductFactory} from "../src/ProductFactory.sol";

contract ProductFactoryTest is Test {
ProductFactory product_factory;
Product template_product;
Product test_product;

address factory_owner;
address product_owner;

function setUp() public {
factory_owner = vm.createWallet(0x42).addr;

template_product = new Product();

vm.prank(factory_owner);
product_factory = new ProductFactory(address(template_product));
assertEq(product_factory.owner(), factory_owner);

product_owner = vm.createWallet(0x99).addr;
vm.prank(product_owner);
test_product = product_factory.createProduct("Test Product", "TEST");
}

function test_product_template() public {
assertEq(product_factory.product_template(), address(template_product));

Product product2 = new Product();
vm.prank(factory_owner);
product_factory.setProgramTemplate(address(product2));
assertEq(product_factory.product_template(), address(product2));
}

function test_create_product() public {
test_product = product_factory.createProduct("Test Product", "TEST");

assertEq(test_product.name(), "Test Product");
assertEq(test_product.symbol(), "TEST");
}

function test_mint_product() public {
assertEq(test_product.owner(), product_owner);

vm.prank(product_owner);
test_product.mint(msg.sender, 2024);
assertEq(test_product.ownerOf(2024), msg.sender);

assertEq(test_product.tokenURI(2024), "");
vm.prank(product_owner);
test_product.setBaseURI("https://example.com/");
assertEq(test_product.tokenURI(2024), "https://example.com/2024");
}

function testFuzz_mint_product(address tokenOwner, uint256 tokenId) public {
vm.prank(product_owner);
test_product.mint(tokenOwner, tokenId);
assertEq(test_product.ownerOf(tokenId), tokenOwner);
}
}

0 comments on commit 8164602

Please sign in to comment.