Skip to content

Commit

Permalink
use upgradeable ERC20 clone
Browse files Browse the repository at this point in the history
  • Loading branch information
XieJunhua committed May 5, 2024
1 parent 8f5eaad commit 3292b80
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 1 deletion.
Binary file modified bun.lockb
Binary file not shown.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"url": "https://github.com/PaulRBerg"
},
"dependencies": {
"@openzeppelin/contracts": "^5.0.1"
"@openzeppelin/contracts": "^5.0.1",
"@openzeppelin/contracts-upgradeable": "^5.0.2"
},
"devDependencies": {
"forge-std": "github:foundry-rs/forge-std#v1.8.1",
Expand Down
68 changes: 68 additions & 0 deletions src/Week3/ERC20Factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

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

event SendETH(bool, bytes);

contract ERC20Factory {
address targetAddress;

uint256 price;

struct ProjectHolder {
uint256 perMint;
address managerAddress;
uint256 price;
uint256 totalSupply;
}

mapping(address => ProjectHolder) tokenMappings;

constructor(address _target) {
targetAddress = _target;
}

function deployInscription(
string memory symbol,
uint256 totalSupply,
uint256 perMint,
uint256 _price
)
public
returns (address)
{
address clone = createClone(targetAddress);
MyERC20(clone).initialize("meme", symbol, totalSupply);
tokenMappings[clone] =
ProjectHolder({ perMint: perMint, managerAddress: msg.sender, price: _price, totalSupply: totalSupply });

return clone;
}

function mintInscription(address tokenAddr) public payable {
uint256 received = msg.value;
require(received >= tokenMappings[tokenAddr].price);
ProjectHolder memory projectHolder = tokenMappings[tokenAddr];
(bool sent, bytes memory data) = projectHolder.managerAddress.call{ value: projectHolder.price / 2 }("");
emit SendETH(sent, data);
MyERC20(tokenAddr).transfer(msg.sender, projectHolder.perMint);
}

function init(string memory symbol, uint256 totalSupply) public returns (address) {
address clone = createClone(targetAddress);
MyERC20(clone).initialize("meme", symbol, totalSupply);
return clone;
}

function createClone(address target) internal returns (address result) {
bytes20 targetBytes = bytes20(target);
assembly {
let clone := mload(0x40)
mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(clone, 0x14), targetBytes)
mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
result := create(0, clone, 0x37)
}
}
}
16 changes: 16 additions & 0 deletions src/Week3/MyERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

// import { Initializable } from "@openzeppelin/contracts/contracts/Initializable.sol";

import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
// import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
// import { ERC20Detailed } from "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";

contract MyERC20 is Initializable, ERC20Upgradeable {
function initialize(string memory name, string memory symbol, uint256 initialSupply) public initializer {
__ERC20_init(name, symbol);
_mint(_msgSender(), initialSupply);
}
}
79 changes: 79 additions & 0 deletions test/ERC20Factory.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.25;

import { Test, console2 } from "forge-std/src/Test.sol";
// import { NFTMarket, BaseERC20, TokenAccessError, ListNFT, InvalidAddressError } from "../src/Week2/NFTMarket.sol";
// import { MyERC2612 } from "../src/Week3/MyERC2612.sol";
// import { TokenBank, BaseERC20 } from "../src/Week2/BaseERC20.sol";
import { MyERC20 } from "../src/Week3/MyERC20.sol";
import { ERC20Factory } from "../src/Week3/ERC20Factory.sol";

// import { SigUtils } from "./SigUtils.sol";
// import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
// import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

contract ERC20FactoryTest is Test {
ERC20Factory erc20Factory;
MyERC20 myERC20;

address alice = makeAddr("alice");
address bob = makeAddr("bob");
address address1 = makeAddr("address1");
address address2 = makeAddr("address2");

function setUp() public {
myERC20 = new MyERC20();
erc20Factory = new ERC20Factory(address(myERC20));
}

function test_init() public {
// newErc20.init("hh", "hh", 1e18);
address clone = erc20Factory.init("hh", 1e18);
MyERC20 newErc20 = MyERC20(clone);

assertEq(newErc20.symbol(), "hh");
assertEq(newErc20.decimals(), 18);
}

function test_deployInscription() public {
vm.prank(alice);

address tokenAddress = erc20Factory.deployInscription("hh", 1e18, 1e5, 1e5);
assertEq(MyERC20(tokenAddress).balanceOf(address(erc20Factory)), 1e18);
uint256 balanceAliceBeforeMint = alice.balance;
uint256 balanceFactoryBeforeMint = address(erc20Factory).balance;

vm.deal(bob, 1 ether);
vm.prank(bob);
// bob mint once, want to get 1e5 token
erc20Factory.mintInscription{ value: 1e5 }(tokenAddress);

assertEq(MyERC20(tokenAddress).balanceOf(address(bob)), 1e5);
assertEq(alice.balance, balanceAliceBeforeMint + 1e5 / 2); // token manager alice get 1e5 wei
assertEq(address(erc20Factory).balance, balanceFactoryBeforeMint + 1e5 / 2); // factory get 1e5 wei

vm.prank(address1);
address anotherTokenAddress = erc20Factory.deployInscription("gg", 1e18, 1e6, 1e6);
assertEq(MyERC20(anotherTokenAddress).balanceOf(address(erc20Factory)), 1e18);

balanceFactoryBeforeMint = address(erc20Factory).balance;
vm.deal(address2, 1 ether);
vm.prank(address2);
erc20Factory.mintInscription{ value: 1e6 }(anotherTokenAddress);
assertEq(MyERC20(anotherTokenAddress).balanceOf(address(address2)), 1e6);

assertEq(address1.balance, 1e6 / 2); // token manager alice get 1e5 wei
assertEq(address(erc20Factory).balance, balanceFactoryBeforeMint + 1e6 / 2); // factory get 1e5 wei
}

function createClone(address target) internal returns (address result) {
bytes20 targetBytes = bytes20(target);
assembly {
let clone := mload(0x40)
mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(clone, 0x14), targetBytes)
mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
result := create(0, clone, 0x37)
}
}
}

0 comments on commit 3292b80

Please sign in to comment.