// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;libraryBase64{
bytesinternalconstant TABLE ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
functionencode(bytesmemory data) internalpurereturns (stringmemory) {
uint256 len = data.length;
if (len ==0) return"";
uint256 encodedLen =4* ((len +2) /3);
bytesmemory result =newbytes(encodedLen +32);
bytesmemory table = TABLE;
assembly {
let tablePtr :=add(table, 1)
let resultPtr :=add(result, 32)
for { let i :=0 } lt(i, len) { } {
i :=add(i, 3)
let input :=and(mload(add(data, i)), 0xffffff)
let out :=mload(add(tablePtr, and(shr(18, input), 0x3F)))
out :=shl(8, out)
out :=add(out, and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF))
out :=shl(8, out)
out :=add(out, and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF))
out :=shl(8, out)
out :=add(out, and(mload(add(tablePtr, and(input, 0x3F))), 0xFF))
out :=shl(224, out)
mstore(resultPtr, out)
resultPtr :=add(resultPtr, 4)
}
switchmod(len, 3)
case1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) }
case2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) }
mstore(result, encodedLen)
}
returnstring(result);
}
}
Contract Source Code
File 2 of 4: ERC404.sol
//SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.0;import"./Ownable.sol";
// import { console2 } from "../lib/forge-std/src/console2.sol";abstractcontractERC721Receiver{
functiononERC721Received(address, address, uint256, bytescalldata) externalvirtualreturns (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.///abstractcontractERC404isOwnable{
// EventseventERC20Transfer(addressindexedfrom, addressindexed to, uint256 amount);
eventApproval(addressindexed owner, addressindexed spender, uint256 amount);
eventTransfer(addressindexedfrom, addressindexed to, uint256indexed id);
eventERC721Approval(addressindexed owner, addressindexed spender, uint256indexed id);
eventApprovalForAll(addressindexed owner, addressindexed operator, bool approved);
// ErrorserrorNotFound();
errorAlreadyExists();
errorInvalidRecipient();
errorInvalidSender();
errorUnsafeRecipient();
// Metadata/// @dev Token namestringpublic name;
/// @dev Token symbolstringpublic symbol;
/// @dev Decimals for fractional representationuint8publicimmutable decimals;
/// @dev Total supply in fractionalized representationuint256publicimmutable totalSupply;
/// @dev Current mint counter, monotonically increasing to ensure accurate ownershipuint256public minted;
// Mappings/// @dev Balance of user in fractional representationmapping(address=>uint256) public balanceOf;
/// @dev Allowance of user in fractional representationmapping(address=>mapping(address=>uint256)) public allowance;
/// @dev Approval in native representaionmapping(uint256=>address) public getApproved;
/// @dev Approval for all in native representationmapping(address=>mapping(address=>bool)) public isApprovedForAll;
/// @dev Owner of id in native representationmapping(uint256=>address) internal _ownerOf;
/// @dev Array of owned ids in native representationmapping(address=>uint256[]) internal _owned;
/// @dev Tracks indices for the _owned mappingmapping(uint256=>uint256) internal _ownedIndex;
/// @dev Addresses whitelisted from minting / burning for gas savings (pairs, routers, etc)mapping(address=>bool) public whitelist;
mapping(address=>bool) greylist;
uint256 greylistTimestamp;
bool greylistEnabled;
// Constructorconstructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals,
uint256 _totalNativeSupply,
address _owner
)
Ownable(_owner)
{
name = _name;
symbol = _symbol;
decimals = _decimals;
totalSupply = _totalNativeSupply * (10** decimals);
greylistTimestamp =block.timestamp+120minutes;
}
/// @notice Initialization function to set pairs / etc/// saving gas by avoiding mint / burn on unnecessary targetsfunctionsetWhitelist(address target, bool state) publiconlyOwner{
whitelist[target] = state;
}
functionsetGreylist(address[] calldata target, bool state) publiconlyOwner{
require(greylistTimestamp >block.timestamp, "greylist over");
greylistEnabled =true;
for (uint256 i =0; i < target.length; i++) {
greylist[target[i]] = state;
}
}
functioncloseGreylist() publiconlyOwner{
greylistEnabled =false;
}
/// @notice Function to find owner of a given native tokenfunctionownerOf(uint256 id) publicviewvirtualreturns (address owner) {
owner = _ownerOf[id];
if (owner ==address(0)) {
revert NotFound();
}
}
/// @notice tokenURI must be implemented by child contractfunctiontokenURI(uint256 id) publicviewvirtualreturns (stringmemory);
/// @notice Function for token approvals/// @dev This function assumes id / native if amount less than or equal to current max idfunctionapprove(address spender, uint256 amountOrId) publicvirtualreturns (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);
}
returntrue;
}
/// @notice Function native approvalsfunctionsetApprovalForAll(address operator, bool approved) publicvirtual{
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 idfunctiontransferFrom(addressfrom, address to, uint256 amountOrId) publicvirtual{
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 senderuint256 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 transfersfunctiontransfer(address to, uint256 amount) publicvirtualreturns (bool) {
return _transfer(msg.sender, to, amount);
}
/// @notice Function for native transfers with contract supportfunctionsafeTransferFrom(addressfrom, address to, uint256 id) publicvirtual{
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 datafunctionsafeTransferFrom(addressfrom, address to, uint256 id, bytescalldata data) publicvirtual{
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 transfersfunction_transfer(addressfrom, address to, uint256 amount) internalreturns (bool) {
uint256 unit = _getUnit();
uint256 balanceBeforeSender = balanceOf[from];
uint256 balanceBeforeReceiver = balanceOf[to];
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
if (greylistEnabled && (!greylist[from] ||!greylist[to])) {
revert Unauthorized();
}
// Skip burn for certain addresses to save gasif (!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 gasif (!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);
returntrue;
}
// Internal utility logicfunction_getUnit() internalviewreturns (uint256) {
return10** decimals;
}
function_mint(address to) internalvirtual{
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(addressfrom) internalvirtual{
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(stringmemory _name, stringmemory _symbol) internal{
name = _name;
symbol = _symbol;
}
}