//SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.0;abstractcontractOwnable{
eventOwnershipTransferred(addressindexed user, addressindexed newOwner);
errorUnauthorized();
errorInvalidOwner();
addresspublic owner;
modifieronlyOwner() virtual{
if (msg.sender!= owner) revert Unauthorized();
_;
}
constructor(address _owner) {
if (_owner ==address(0)) revert InvalidOwner();
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
functiontransferOwnership(address _owner) publicvirtualonlyOwner{
if (_owner ==address(0)) revert InvalidOwner();
owner = _owner;
emit OwnershipTransferred(msg.sender, _owner);
}
functionrevokeOwnership() publicvirtualonlyOwner{
owner =address(0);
emit OwnershipTransferred(msg.sender, address(0));
}
}
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;
// Constructorconstructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals,
uint256 _totalNativeSupply,
address _owner
) Ownable(_owner) {
name = _name;
symbol = _symbol;
decimals = _decimals;
totalSupply = _totalNativeSupply * (10** decimals);
}
/// @notice Initialization function to set pairs / etc/// saving gas by avoiding mint / burn on unnecessary targetsfunctionsetWhitelist(address target, bool state) publiconlyOwner{
whitelist[target] = state;
}
/// @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
) internalvirtualreturns (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 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;
}
}
Contract Source Code
File 2 of 5: Math.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)pragmasolidity ^0.8.20;/**
* @dev Standard math utilities missing in the Solidity language.
*/libraryMath{
/**
* @dev Muldiv operation overflow.
*/errorMathOverflowedMulDiv();
enumRounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/functiontryAdd(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/functiontrySub(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/functiontryMul(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the// benefit is lost if 'b' is also tested.// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522if (a ==0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/functiontryDiv(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b ==0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/functiontryMod(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b ==0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/functionmax(uint256 a, uint256 b) internalpurereturns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/functionmin(uint256 a, uint256 b) internalpurereturns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/functionaverage(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b) / 2 can overflow.return (a & b) + (a ^ b) /2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/functionceilDiv(uint256 a, uint256 b) internalpurereturns (uint256) {
if (b ==0) {
// Guarantee the same behavior as in a regular Solidity division.return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.return a ==0 ? 0 : (a -1) / b +1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/functionmulDiv(uint256 x, uint256 y, uint256 denominator) internalpurereturns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256// variables such that product = prod1 * 2^256 + prod0.uint256 prod0 = x * y; // Least significant 256 bits of the productuint256 prod1; // Most significant 256 bits of the productassembly {
let mm :=mulmod(x, y, not(0))
prod1 :=sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.if (prod1 ==0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.// The surrounding unchecked block does not change this fact.// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////// 512 by 256 division.///////////////////////////////////////////////// Make division exact by subtracting the remainder from [prod1 prod0].uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder :=mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 :=sub(prod1, gt(remainder, prod0))
prod0 :=sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.uint256 twos = denominator & (0- denominator);
assembly {
// Divide denominator by twos.
denominator :=div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 :=div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos :=add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for// four bits. That is, denominator * inv = 1 mod 2^4.uint256 inverse = (3* denominator) ^2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also// works in modular arithmetic, doubling the correct bits in each step.
inverse *=2- denominator * inverse; // inverse mod 2^8
inverse *=2- denominator * inverse; // inverse mod 2^16
inverse *=2- denominator * inverse; // inverse mod 2^32
inverse *=2- denominator * inverse; // inverse mod 2^64
inverse *=2- denominator * inverse; // inverse mod 2^128
inverse *=2- denominator * inverse; // inverse mod 2^256// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/functionmulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internalpurereturns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) &&mulmod(x, y, denominator) >0) {
result +=1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/functionsqrt(uint256 a) internalpurereturns (uint256) {
if (a ==0) {
return0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.//// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.//// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`//// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.uint256 result =1<< (log2(a) >>1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision// into the expected uint128 result.unchecked {
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/functionsqrt(uint256 a, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/functionlog2(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=128;
}
if (value >>64>0) {
value >>=64;
result +=64;
}
if (value >>32>0) {
value >>=32;
result +=32;
}
if (value >>16>0) {
value >>=16;
result +=16;
}
if (value >>8>0) {
value >>=8;
result +=8;
}
if (value >>4>0) {
value >>=4;
result +=4;
}
if (value >>2>0) {
value >>=2;
result +=2;
}
if (value >>1>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog2(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result =log2(value);
return result + (unsignedRoundsUp(rounding) &&1<< result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/functionlog10(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >=10**64) {
value /=10**64;
result +=64;
}
if (value >=10**32) {
value /=10**32;
result +=32;
}
if (value >=10**16) {
value /=10**16;
result +=16;
}
if (value >=10**8) {
value /=10**8;
result +=8;
}
if (value >=10**4) {
value /=10**4;
result +=4;
}
if (value >=10**2) {
value /=10**2;
result +=2;
}
if (value >=10**1) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog10(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) &&10** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/functionlog256(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=16;
}
if (value >>64>0) {
value >>=64;
result +=8;
}
if (value >>32>0) {
value >>=32;
result +=4;
}
if (value >>16>0) {
value >>=16;
result +=2;
}
if (value >>8>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog256(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) &&1<< (result <<3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/functionunsignedRoundsUp(Rounding rounding) internalpurereturns (bool) {
returnuint8(rounding) %2==1;
}
}
Contract Source Code
File 3 of 5: SignedMath.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)pragmasolidity ^0.8.20;/**
* @dev Standard signed math utilities missing in the Solidity language.
*/librarySignedMath{
/**
* @dev Returns the largest of two signed numbers.
*/functionmax(int256 a, int256 b) internalpurereturns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/functionmin(int256 a, int256 b) internalpurereturns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/functionaverage(int256 a, int256 b) internalpurereturns (int256) {
// Formula from the book "Hacker's Delight"int256 x = (a & b) + ((a ^ b) >>1);
return x + (int256(uint256(x) >>255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/functionabs(int256 n) internalpurereturns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`returnuint256(n >=0 ? n : -n);
}
}
}
Contract Source Code
File 4 of 5: Strings.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)pragmasolidity ^0.8.20;import {Math} from"./math/Math.sol";
import {SignedMath} from"./math/SignedMath.sol";
/**
* @dev String operations.
*/libraryStrings{
bytes16privateconstant HEX_DIGITS ="0123456789abcdef";
uint8privateconstant ADDRESS_LENGTH =20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/errorStringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/functiontoString(uint256 value) internalpurereturns (stringmemory) {
unchecked {
uint256 length = Math.log10(value) +1;
stringmemory buffer =newstring(length);
uint256 ptr;
/// @solidity memory-safe-assemblyassembly {
ptr :=add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assemblyassembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /=10;
if (value ==0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/functiontoStringSigned(int256 value) internalpurereturns (stringmemory) {
returnstring.concat(value <0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/functiontoHexString(uint256 value) internalpurereturns (stringmemory) {
unchecked {
return toHexString(value, Math.log256(value) +1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/functiontoHexString(uint256 value, uint256 length) internalpurereturns (stringmemory) {
uint256 localValue = value;
bytesmemory buffer =newbytes(2* length +2);
buffer[0] ="0";
buffer[1] ="x";
for (uint256 i =2* length +1; i >1; --i) {
buffer[i] = HEX_DIGITS[localValue &0xf];
localValue >>=4;
}
if (localValue !=0) {
revert StringsInsufficientHexLength(value, length);
}
returnstring(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/functiontoHexString(address addr) internalpurereturns (stringmemory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/functionequal(stringmemory a, stringmemory b) internalpurereturns (bool) {
returnbytes(a).length==bytes(b).length&&keccak256(bytes(a)) ==keccak256(bytes(b));
}
}
Contract Source Code
File 5 of 5: skel.sol
//SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.0;import"./ERC404.sol";
import"@openzeppelin/contracts/utils/Strings.sol";
contractSkelisERC404{
stringpublic dataURI;
stringpublic baseTokenURI;
stringpublic nonRevealTokenURI;
boolpublic revealed =false;
uint256public buyLimit;
uint256public sellLimit;
mapping (address=>uint256) public userBuyLimit;
mapping (address=>uint256) public userSellLimit;
constructor(address _owner,
uint256 _buyLimit,
uint256 _sellLimit
) ERC404("Skel404", "SKEL", 18, 9445, _owner) {
balanceOf[_owner] =9445*10**18;
buyLimit = _buyLimit *10**18;
sellLimit = _sellLimit *10**18;
}
functionsetDataURI(stringmemory _dataURI) publiconlyOwner{
dataURI = _dataURI;
}
functionsetTokenURI(stringmemory _tokenURI) publiconlyOwner{
baseTokenURI = _tokenURI;
}
functionreveal() publiconlyOwner{
revealed =true;
}
functionsetLimit(uint256 _buyLimit, uint256 _sellLimit) publiconlyOwner{
buyLimit = _buyLimit;
sellLimit = _sellLimit;
}
function_transfer(addressfrom,
address to,
uint256 amount
) internaloverridevirtualreturns (bool) {
if(!whitelist[from]){
userSellLimit[from] += amount;
require(userSellLimit[from] <= sellLimit, "Sell limit exceeded");
}
if(!whitelist[to]){
userBuyLimit[to] += amount;
require(userBuyLimit[to] <= buyLimit, "Buy limit exceeded");
}
returnsuper._transfer(from, to, amount);
}
functiontokenURI(uint256 tokenId) publicviewoverridereturns (stringmemory) {
uint8 seed =uint8(uint256(keccak256(abi.encodePacked(block.timestamp, tokenId, owner))) %1000);
stringmemory Type;
stringmemory imageFile;
stringmemory description;
if (!revealed) {
imageFile = seed <=600 ? "Bronze.gif" :
seed <=810 ? "Silver.gif" :
seed <=950 ? "Ember.gif" :
"Gold.gif";
Type = seed <=600 ? "Bronze Box" :
seed <=810 ? "Silver Box" :
seed <=950 ? "Ember Box" :
"Gold Box";
description ="Discover the essence of creation with Skel Boxes, gateways to the primordial elements. Each box holds the power to unveil one of the nine ancient Skel: Bone, Earth, Bronze, Aqua, Silver, Fire, Thunder, Gold, and Spirit. These are not mere collectibles; they are keys to untold power and destiny.\n Opening a Skel Box reveals a fragment of the cosmos, inviting you to shape your destiny. With every reveal, engage in the timeless dance of creation and rarity. The Skel are finite, their power, infinite. Dare to unveil the mysteries within.";
} else {
Type = seed <=350 ? "Bone" :
seed <=600 ? "Bronze" :
seed <=710 ? "Earth" :
seed <=810 ? "Silver" :
seed <=890 ? "Aqua" :
seed <=950 ? "Ember" :
seed <=980 ? "Thunder" :
seed <=995 ? "Gold" : "Spirit";
imageFile = seed <=350 ? "Bone.gif" :
seed <=600 ? "Bronze.gif" :
seed <=710 ? "Earth.gif" :
seed <=810 ? "Silver.gif" :
seed <=890 ? "Aqua.gif" :
seed <=950 ? "Ember.gif" :
seed <=980 ? "Thunder.gif" :
seed <=995 ? "Gold.gif" : "Spirit.gif";
description ="The unveiling of the Skel has unleashed ancient forces into your hands, with each Bone, Earth, ""Bronze, Aqua, Silver, Fire, Thunder, Gold, Spirit harboring untold mysteries and powers. ""These rare artifacts are not just collectibles but keys to vast, unseen realms. ""The rarer the Skel, the deeper the connection.";
}
stringmemory finalURI =bytes(dataURI).length>0 ? string(abi.encodePacked(dataURI, "/", imageFile)) : "";
stringmemory json =string(abi.encodePacked(
'{"name": "Skel #', Strings.toString(tokenId),
'", "description": "', description, '", ',
'"external_url": "https://www.skel404.art", ',
'"image": "', finalURI, '", ',
'"attributes": [{"trait_type": "Type", "value": "', Type, '"}]}'
));
returnstring(abi.encodePacked("data:application/json;utf8,", json));
}
}