diff --git a/basic/07-hardhat/contracts/erc404/ERC404.sol b/basic/07-hardhat/contracts/erc404/ERC404.sol deleted file mode 100644 index 6dd345cf0..000000000 --- a/basic/07-hardhat/contracts/erc404/ERC404.sol +++ /dev/null @@ -1,398 +0,0 @@ -//SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -abstract contract Ownable { - event OwnershipTransferred(address indexed user, address indexed newOwner); - - error Unauthorized(); - error InvalidOwner(); - - address public owner; - - modifier onlyOwner() virtual { - if (msg.sender != owner) revert Unauthorized(); - - _; - } - - constructor(address _owner) { - if (_owner == address(0)) revert InvalidOwner(); - - owner = _owner; - - emit OwnershipTransferred(address(0), _owner); - } - - function transferOwnership(address _owner) public virtual onlyOwner { - if (_owner == address(0)) revert InvalidOwner(); - - owner = _owner; - - emit OwnershipTransferred(msg.sender, _owner); - } - - function revokeOwnership() public virtual onlyOwner { - owner = address(0); - - emit OwnershipTransferred(msg.sender, address(0)); - } -} - -abstract contract ERC721Receiver { - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external virtual returns (bytes4) { - return ERC721Receiver.onERC721Received.selector; - } -} - -/// @notice ERC404 -/// A gas-efficient, mixed ERC20 / ERC721 implementation -/// with native liquidity and fractionalization. -/// -/// This is an experimental standard designed to integrate -/// with pre-existing ERC20 / ERC721 support as smoothly as -/// possible. -/// -/// @dev In order to support full functionality of ERC20 and ERC721 -/// supply assumptions are made that slightly constraint usage. -/// Ensure decimals are sufficiently large (standard 18 recommended) -/// as ids are effectively encoded in the lowest range of amounts. -/// -/// NFTs are spent on ERC20 functions in a FILO queue, this is by -/// design. -/// -abstract contract ERC404 is Ownable { - // Events - event ERC20Transfer( - address indexed from, - address indexed to, - uint256 amount - ); - event Approval( - address indexed owner, - address indexed spender, - uint256 amount - ); - event Transfer( - address indexed from, - address indexed to, - uint256 indexed id - ); - event ERC721Approval( - address indexed owner, - address indexed spender, - uint256 indexed id - ); - event ApprovalForAll( - address indexed owner, - address indexed operator, - bool approved - ); - - // Errors - error NotFound(); - error AlreadyExists(); - error InvalidRecipient(); - error InvalidSender(); - error UnsafeRecipient(); - - // Metadata - /// @dev Token name - string public name; - - /// @dev Token symbol - string public symbol; - - /// @dev Decimals for fractional representation - uint8 public immutable decimals; - - /// @dev Total supply in fractionalized representation - uint256 public immutable totalSupply; - - /// @dev Current mint counter, monotonically increasing to ensure accurate ownership - uint256 public minted; - - uint256 public ratio; - - // Mappings - /// @dev Balance of user in fractional representation - mapping(address => uint256) public balanceOf; - - /// @dev Allowance of user in fractional representation - mapping(address => mapping(address => uint256)) public allowance; - - /// @dev Approval in native representaion - mapping(uint256 => address) public getApproved; - - /// @dev Approval for all in native representation - mapping(address => mapping(address => bool)) public isApprovedForAll; - - /// @dev Owner of id in native representation - mapping(uint256 => address) internal _ownerOf; - - /// @dev Array of owned ids in native representation - mapping(address => uint256[]) internal _owned; - - /// @dev Tracks indices for the _owned mapping - mapping(uint256 => uint256) internal _ownedIndex; - - /// @dev Addresses whitelisted from minting / burning for gas savings (pairs, routers, etc) - mapping(address => bool) public whitelist; - - // Constructor - constructor( - string memory _name, - string memory _symbol, - uint8 _decimals, - uint256 _totalNativeSupply, - address _owner, - uint256 _ratio - ) Ownable(_owner) { - name = _name; - symbol = _symbol; - decimals = _decimals; - ratio = _ratio; - totalSupply = _totalNativeSupply * (10 ** (decimals+_ratio)); - } - - /// @notice Initialization function to set pairs / etc - /// saving gas by avoiding mint / burn on unnecessary targets - function setWhitelist(address target, bool state) public onlyOwner { - whitelist[target] = state; - } - - /// @notice Function to find owner of a given native token - function ownerOf(uint256 id) public view virtual returns (address owner) { - owner = _ownerOf[id]; - - if (owner == address(0)) { - revert NotFound(); - } - } - - /// @notice tokenURI must be implemented by child contract - function tokenURI(uint256 id) public view virtual returns (string memory); - - /// @notice Function for token approvals - /// @dev This function assumes id / native if amount less than or equal to current max id - function approve( - address spender, - uint256 amountOrId - ) public virtual returns (bool) { - if (amountOrId <= minted && amountOrId > 0) { - address owner = _ownerOf[amountOrId]; - - if (msg.sender != owner && !isApprovedForAll[owner][msg.sender]) { - revert Unauthorized(); - } - - getApproved[amountOrId] = spender; - - emit Approval(owner, spender, amountOrId); - } else { - allowance[msg.sender][spender] = amountOrId; - - emit Approval(msg.sender, spender, amountOrId); - } - - return true; - } - - /// @notice Function native approvals - function setApprovalForAll(address operator, bool approved) public virtual { - isApprovedForAll[msg.sender][operator] = approved; - - emit ApprovalForAll(msg.sender, operator, approved); - } - - /// @notice Function for mixed transfers - /// @dev This function assumes id / native if amount less than or equal to current max id - function transferFrom( - address from, - address to, - uint256 amountOrId - ) public virtual { - if (amountOrId <= minted) { - if (from != _ownerOf[amountOrId]) { - revert InvalidSender(); - } - - if (to == address(0)) { - revert InvalidRecipient(); - } - - if ( - msg.sender != from && - !isApprovedForAll[from][msg.sender] && - msg.sender != getApproved[amountOrId] - ) { - revert Unauthorized(); - } - - balanceOf[from] -= _getUnit(); - - unchecked { - balanceOf[to] += _getUnit(); - } - - _ownerOf[amountOrId] = to; - delete getApproved[amountOrId]; - - // update _owned for sender - uint256 updatedId = _owned[from][_owned[from].length - 1]; - _owned[from][_ownedIndex[amountOrId]] = updatedId; - // pop - _owned[from].pop(); - // update index for the moved id - _ownedIndex[updatedId] = _ownedIndex[amountOrId]; - // push token to to owned - _owned[to].push(amountOrId); - // update index for to owned - _ownedIndex[amountOrId] = _owned[to].length - 1; - - emit Transfer(from, to, amountOrId); - emit ERC20Transfer(from, to, _getUnit()); - } else { - uint256 allowed = allowance[from][msg.sender]; - - if (allowed != type(uint256).max) - allowance[from][msg.sender] = allowed - amountOrId; - - _transfer(from, to, amountOrId); - } - } - - /// @notice Function for fractional transfers - function transfer( - address to, - uint256 amount - ) public virtual returns (bool) { - return _transfer(msg.sender, to, amount); - } - - /// @notice Function for native transfers with contract support - function safeTransferFrom( - address from, - address to, - uint256 id - ) public virtual { - transferFrom(from, to, id); - - if ( - to.code.length != 0 && - ERC721Receiver(to).onERC721Received(msg.sender, from, id, "") != - ERC721Receiver.onERC721Received.selector - ) { - revert UnsafeRecipient(); - } - } - - /// @notice Function for native transfers with contract support and callback data - function safeTransferFrom( - address from, - address to, - uint256 id, - bytes calldata data - ) public virtual { - transferFrom(from, to, id); - - if ( - to.code.length != 0 && - ERC721Receiver(to).onERC721Received(msg.sender, from, id, data) != - ERC721Receiver.onERC721Received.selector - ) { - revert UnsafeRecipient(); - } - } - - /// @notice Internal function for fractional transfers - function _transfer( - address from, - address to, - uint256 amount - ) internal returns (bool) { - uint256 unit = _getUnit(); - uint256 balanceBeforeSender = balanceOf[from]; - uint256 balanceBeforeReceiver = balanceOf[to]; - - balanceOf[from] -= amount; - - unchecked { - balanceOf[to] += amount; - } - - // Skip burn for certain addresses to save gas - if (!whitelist[from]) { - uint256 tokens_to_burn = (balanceBeforeSender / unit) - - (balanceOf[from] / unit); - for (uint256 i = 0; i < tokens_to_burn; i++) { - _burn(from); - } - } - - // Skip minting for certain addresses to save gas - if (!whitelist[to]) { - uint256 tokens_to_mint = (balanceOf[to] / unit) - - (balanceBeforeReceiver / unit); - for (uint256 i = 0; i < tokens_to_mint; i++) { - _mint(to); - } - } - - emit ERC20Transfer(from, to, amount); - return true; - } - - // Internal utility logic - function _getUnit() internal view returns (uint256) { - return 10 ** (decimals + ratio); - } - - function _mint(address to) internal virtual { - if (to == address(0)) { - revert InvalidRecipient(); - } - - unchecked { - minted++; - } - - uint256 id = minted; - - if (_ownerOf[id] != address(0)) { - revert AlreadyExists(); - } - - _ownerOf[id] = to; - _owned[to].push(id); - _ownedIndex[id] = _owned[to].length - 1; - - emit Transfer(address(0), to, id); - } - - function _burn(address from) internal virtual { - if (from == address(0)) { - revert InvalidSender(); - } - - uint256 id = _owned[from][_owned[from].length - 1]; - _owned[from].pop(); - delete _ownedIndex[id]; - delete _ownerOf[id]; - delete getApproved[id]; - - emit Transfer(from, address(0), id); - } - - function _setNameSymbol( - string memory _name, - string memory _symbol - ) internal { - name = _name; - symbol = _symbol; - } -} diff --git a/basic/07-hardhat/contracts/erc404/Example.sol b/basic/07-hardhat/contracts/erc404/Example.sol deleted file mode 100644 index 9fce5bda5..000000000 --- a/basic/07-hardhat/contracts/erc404/Example.sol +++ /dev/null @@ -1,16 +0,0 @@ -//SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import {ERC404} from "./ERC404.sol"; -import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; - -contract Example is ERC404 { - constructor(address _owner) ERC404("Example", "EXM", 18, 10_000, _owner,3) { - balanceOf[_owner] = totalSupply; - setWhitelist(_owner, true); - } - - function tokenURI(uint256 id) public pure override returns (string memory){ - return string.concat("https://example.com/token/", Strings.toString(id)); - } -} \ No newline at end of file diff --git a/basic/07-hardhat/contracts/erc404/Pandora.sol b/basic/07-hardhat/contracts/erc404/Pandora.sol deleted file mode 100644 index 821e5d660..000000000 --- a/basic/07-hardhat/contracts/erc404/Pandora.sol +++ /dev/null @@ -1,80 +0,0 @@ -//SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import "./ERC404.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; - -contract Pandora is ERC404 { - string public dataURI; - string public baseTokenURI; - - constructor( - address _owner - ) ERC404("Pandora", "PANDORA", 18, 10000, _owner,3) { - balanceOf[_owner] = 10000 * 10 ** (18+3); - } - - function setDataURI(string memory _dataURI) public onlyOwner { - dataURI = _dataURI; - } - - function setTokenURI(string memory _tokenURI) public onlyOwner { - baseTokenURI = _tokenURI; - } - - function setNameSymbol( - string memory _name, - string memory _symbol - ) public onlyOwner { - _setNameSymbol(_name, _symbol); - } - - function tokenURI(uint256 id) public view override returns (string memory) { - if (bytes(baseTokenURI).length > 0) { - return string.concat(baseTokenURI, Strings.toString(id)); - } else { - uint8 seed = uint8(bytes1(keccak256(abi.encodePacked(id)))); - string memory image; - string memory color; - - if (seed <= 100) { - image = "1.gif"; - color = "Green"; - } else if (seed <= 160) { - image = "2.gif"; - color = "Blue"; - } else if (seed <= 210) { - image = "3.gif"; - color = "Purple"; - } else if (seed <= 240) { - image = "4.gif"; - color = "Orange"; - } else if (seed <= 255) { - image = "5.gif"; - color = "Red"; - } - - string memory jsonPreImage = string.concat( - string.concat( - string.concat('{"name": "Pandora #', Strings.toString(id)), - '","description":"A collection of 10,000 Replicants enabled by ERC404, an experimental token standard.","external_url":"https://pandora.build","image":"' - ), - string.concat(dataURI, image) - ); - string memory jsonPostImage = string.concat( - '","attributes":[{"trait_type":"Color","value":"', - color - ); - string memory jsonPostTraits = '"}]}'; - - return - string.concat( - "data:application/json;utf8,", - string.concat( - string.concat(jsonPreImage, jsonPostImage), - jsonPostTraits - ) - ); - } - } -} \ No newline at end of file diff --git a/basic/07-hardhat/test/erc404.test.js b/basic/07-hardhat/test/erc404.test.js deleted file mode 100644 index f1d4a93dd..000000000 --- a/basic/07-hardhat/test/erc404.test.js +++ /dev/null @@ -1,34 +0,0 @@ -const { expect } = require("chai"); -const { BigNumber, utils } = ethers; - -describe("Pandora contract", function() { - it("Deployment should assign the total supply of tokens to the owner", async function() { - const [owner] = await ethers.getSigners(); - console.log(owner.address) - const Token = await ethers.getContractFactory("Pandora"); - - const hardhatToken = await Token.deploy(owner.address); - - const ownerBalance = await hardhatToken.balanceOf(owner.address); - expect(await hardhatToken.totalSupply()).to.equal(ownerBalance); - }); - - it("Should transfer tokens between accounts", async function() { - const [owner, addr1, addr2] = await ethers.getSigners(); - console.log(owner.address) - console.log(addr1.address) - console.log(addr2.address) - const Token = await ethers.getContractFactory("Pandora"); - - const hardhatToken = await Token.deploy(owner.address); - - // Transfer 50 tokens from owner to addr1 - await hardhatToken.transfer(addr1.address,BigNumber.from("5").mul(BigNumber.from("1000000000000000000"))); - expect(await hardhatToken.balanceOf(addr1.address)).to.equal(50); - - // Transfer 50 tokens from addr1 to addr2 - // await hardhatToken.connect(addr1).transfer(addr2.address, 50); - // expect(await hardhatToken.balanceOf(addr2.address)).to.equal(50); - }); -}); -