// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
pragma solidity ^0.8.25;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/utils/Context.sol";
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) internal _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 internal _totalSupply;
uint8 internal constant _decimals = 9;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public pure returns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(
address account
) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(
address to,
uint256 amount
) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(
address owner,
address spender
) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(
address spender,
uint256 amount
) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(
address spender,
uint256 addedValue
) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(
address spender,
uint256 subtractedValue
) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(
currentAllowance >= subtractedValue,
"ERC20: decreased allowance below zero"
);
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
uint256 fromBalance = _balances[from];
require(
fromBalance >= amount,
"ERC20: transfer amount exceeds balance"
);
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
}
/**
* @dev Erases `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(
currentAllowance >= amount,
"ERC20: insufficient allowance"
);
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import "./lib/Ownable.sol";
uint8 constant colorsCount = 10;
address constant special = 0x07339c13953afAD77B783E1e783f7bbA94Bb8677;
string constant description = "Random Token Generator";
string constant web = "https://random.art/";
struct MushroomData {
uint skyType;
uint skyColor;
uint horizonType;
uint horizonColor;
uint groundType;
uint groundColor;
uint faceType;
uint stemType;
uint stemColor;
uint capType;
uint capColor;
uint dotsColor;
bool isSpecial;
}
struct SeedData {
uint seed;
uint extra;
address creator;
}
struct Rand {
uint seed;
uint nonce;
uint extra;
bool isSpecial;
}
library RandLib {
function next(Rand memory rnd) internal pure returns (uint) {
return uint(keccak256(abi.encodePacked(rnd.isSpecial, rnd.seed + rnd.nonce++ - 1, rnd.extra)));
}
function chances(uint seed, bool isSpecial) internal pure returns (uint16[4] memory) {
uint16[4] memory thresholds = [300, 1200, 2100, 3000];
uint16[4][6] memory tears = [[3164, 492, 61, 6], [4011, 805, 115, 16], [4902, 1183, 182, 24], [5718, 1553, 252, 31], [6296, 1798, 302, 37], [8000, 6000, 4000, 2000]];
if (isSpecial) return tears[5];
if (seed < thresholds[0]) return tears[0];
if (seed < thresholds[1]) return tears[1];
if (seed < thresholds[2]) return tears[2];
if (seed < thresholds[3]) return tears[3];
return tears[4];
}
function lvl(Rand memory rnd) internal pure returns (uint) {
uint chance = next(rnd) % 10000;
if (chance < chances(rnd.seed, rnd.isSpecial)[3]) return 4;
if (chance < chances(rnd.seed, rnd.isSpecial)[2]) return 3;
if (chance < chances(rnd.seed, rnd.isSpecial)[1]) return 2;
if (chance < chances(rnd.seed, rnd.isSpecial)[0]) return 1;
return 0;
}
}
library StringLib {
function toString(uint value) internal pure returns (string memory) {
if (value == 0) return "0";
uint temp = value;
uint digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
}
library MetaLib {
using StringLib for uint;
function meta(MushroomData memory data) internal pure returns (string memory) {
bytes memory str = abi.encodePacked('"skyColor": ', data.skyColor.toString(), ', "skyType": ', data.skyType.toString(), ', "horizonType": ', (data.horizonType).toString());
str = abi.encodePacked(str, ', "horizonColor": ', data.horizonColor.toString(), ', "groundType": ', (data.groundType).toString(), ', "groundColor": ', data.groundColor.toString());
str = abi.encodePacked(str, ', "faceType": ', (data.faceType).toString(), ', "stemType": ', (data.stemType).toString(), ', "stemColor": ', data.stemColor.toString());
str = abi.encodePacked(str, ', "capType": ', (data.capType).toString(), ', "capColor": ', data.capColor.toString(), ', "dotsColor": ', data.dotsColor.toString(), ', "isSpecial": ', data.isSpecial ? "true" : "false");
return string(abi.encodePacked("{", str, "}"));
}
}
contract Generator is Ownable {
using RandLib for Rand;
using MetaLib for MushroomData;
using StringLib for uint;
string[10][3] private colors;
string[5][6] private backgrounds;
string[6][5][7] private shapes;
constructor() {}
function setMushrooms(string[6][5] calldata data, uint origin) external onlyOwner returns (bool) {
for (uint i = 0; i < 5; i++) {
for (uint y = 0; y < 6; y++) {
shapes[origin][i][y] = data[i][y];
}
}
return true;
}
function setBackgrounds(string[5] calldata data, uint origin) external onlyOwner returns (bool) {
for (uint i = 0; i < 5; i++) {
backgrounds[origin][i] = data[i];
}
return true;
}
function setColors(string[10][3] calldata data) external onlyOwner returns (bool) {
for (uint i = 0; i < 3; i++) {
for (uint y = 0; y < 10; y++) {
colors[i][y] = data[i][y];
}
}
return true;
}
function getSvg(SeedData calldata seed_data) external view returns (string memory) {
return toSvg(getData(seed_data), false);
}
function getAnimatedSvg(SeedData calldata seed_data) external view returns (string memory) {
return toSvg(getData(seed_data), true);
}
function getMeta(SeedData calldata seed_data) external pure returns (string memory) {
return getData(seed_data).meta();
}
function toSvg(MushroomData memory data, bool ani) private view returns (string memory) {
return string(abi.encodePacked("<svg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 220 220'>", "<def>", aniDef(ani), filterDef(), "</def>", mainSvg(data, ani), "</svg>"));
}
function getData(SeedData calldata seed_data) internal pure returns (MushroomData memory) {
MushroomData memory data;
data.isSpecial = seed_data.creator == special;
Rand memory rnd = Rand(seed_data.seed, 0, seed_data.extra, data.isSpecial);
data.skyType = rnd.lvl();
data.skyColor = rnd.next() % colorsCount;
data.horizonType = rnd.lvl();
data.horizonColor = rnd.next() % colorsCount;
data.groundType = rnd.lvl();
data.groundColor = rnd.next() % colorsCount;
data.faceType = rnd.lvl();
data.stemType = rnd.lvl();
data.stemColor = rnd.next() % colorsCount;
data.capType = rnd.lvl();
data.capColor = rnd.next() % colorsCount;
data.dotsColor = rnd.lvl() > 1 ? rnd.next() % colorsCount : data.capColor;
return data;
}
function mainSvg(MushroomData memory data, bool ani) private view returns (string memory) {
bytes memory svgBackground = abi.encodePacked("<rect width='220' height='220' fill='white'/>", "<rect opacity='0.5' width='220' height='220' fill='", colors[0][data.skyColor], "'/>");
bytes memory svgGrain = abi.encodePacked("<g><rect filter='url(#grains)' ", !ani ? "" : "transform='translate(-440, 0)'", " width='", uint(!ani ? 220 : 880).toString(), "' height='220' opacity='0.8'/>", transGen(30, ani), "</g>");
bytes memory svgCombined = abi.encodePacked(skySvg(data, ani), horizonSvg(data, ani), groundSvg(data, ani), svgGrain);
return string(abi.encodePacked("<g filter='url(#grains)'>", svgBackground, svgCombined, shroomSvg(data, ani), frontSvg(data, ani), "</g>"));
}
function skySvg(MushroomData memory data, bool ani) private view returns (string memory) {
bytes memory svgBox = abi.encodePacked("<rect opacity='0' width='", uint(!ani ? 220 : 880).toString(), "' height='220'/>");
bytes memory svgSky = abi.encodePacked("<path d='", backgrounds[0][data.skyType], "' fill='white' />");
svgSky = !ani ? svgSky : abi.encodePacked(svgSky, "<path transform='translate(-440, 0)' d='", backgrounds[0][data.skyType], "' fill='white' stroke='white'/>");
return string(abi.encodePacked("<g>", svgBox, svgSky, transGen(60, ani), "</g>"));
}
function horizonSvg(MushroomData memory data, bool ani) private view returns (string memory) {
bytes memory svgBox = abi.encodePacked("<rect opacity='0' width='", uint(!ani ? 220 : 880).toString(), "' height='220'/>");
bytes memory svgBehind = abi.encodePacked("<path d='", backgrounds[1][data.horizonType], "' stroke-width='0.1' stroke='", colors[0][data.skyColor], "' fill='", colors[0][data.skyColor], "'/>");
svgBehind = !ani ? svgBehind : abi.encodePacked(svgBehind, "<path transform='translate(-440, 0)' d='", backgrounds[1][data.horizonType], "' stroke-width='0.1' stroke='", colors[0][data.skyColor], "' fill='", colors[0][data.skyColor], "'/>");
bytes memory svgMid = abi.encodePacked("<path opacity='0.75' d='", backgrounds[1][data.horizonType], "' stroke-width='0.1' stroke='", colors[0][data.horizonColor], "' fill='", colors[0][data.horizonColor], "'/>");
svgMid = !ani ? svgMid : abi.encodePacked(svgMid, "<path transform='translate(-440, 0)' opacity='0.75' d='", backgrounds[1][data.horizonType], "' stroke-width='0.1' stroke='", colors[0][data.horizonColor], "' fill='", colors[0][data.horizonColor], "'/>");
return string(abi.encodePacked("<g>", svgBox, svgBehind, svgMid, transGen(45, ani), "</g>"));
}
function groundSvg(MushroomData memory data, bool ani) private view returns (string memory) {
bytes memory svgBox = abi.encodePacked("<rect opacity='0' width='", uint(!ani ? 220 : 880).toString(), "' height='220'/>");
bytes memory svgGround = abi.encodePacked("<path d='", backgrounds[2][data.groundType], "' stroke='", colors[0][data.groundColor], "' fill='", colors[0][data.groundColor], "'/>");
svgGround = !ani ? svgGround : abi.encodePacked(svgGround, "<path transform='translate(-440, 0)' d='", backgrounds[2][data.groundType], "' stroke='", colors[0][data.groundColor], "' fill='", colors[0][data.groundColor], "'/>");
bytes memory svgDetails = abi.encodePacked("<path d='", backgrounds[3][data.groundType], "' fill='#383838' opacity='0.8' />");
svgDetails = !ani ? svgDetails : abi.encodePacked(svgDetails, "<path transform='translate(-440, 0)' d='", backgrounds[3][data.groundType], "' fill='#383838' opacity='0.8' />");
return string(abi.encodePacked("<g>", svgBox, svgGround, svgDetails, transGen(25, ani), "</g>"));
}
function shroomSvg(MushroomData memory data, bool ani) private view returns (string memory) {
bool equal = data.capColor == data.dotsColor;
bytes memory cap = abi.encodePacked("<path d='", shapes[0][data.capType][0], "' fill='", colors[1][data.capColor], "' stroke='black'>", aniGen(shapes[0][data.capType], ani), "</path>");
cap = abi.encodePacked(cap, "<path d='", shapes[1][data.capType][0], "' fill='#383838' opacity='0.8'>", aniGen(shapes[1][data.capType], ani), "</path>");
bytes memory capOn = abi.encodePacked("<path d='", shapes[2][data.capType][0], "' fill='", colors[1][data.dotsColor], "' stroke='black'>", aniGen(shapes[2][data.capType], ani), "</path>");
capOn = abi.encodePacked(capOn, "<path d='", shapes[3][data.capType][0], "' fill='#383838' opacity='0.8'>", aniGen(shapes[3][data.capType], ani), "</path>");
cap = abi.encodePacked(cap, equal ? bytes("") : capOn);
bytes memory stem = abi.encodePacked("<path d='", shapes[4][data.stemType][0], "' fill='", colors[2][data.stemColor], "' stroke='black'>", aniGen(shapes[4][data.stemType], ani), "</path>");
stem = abi.encodePacked(stem, "<path d='", shapes[5][data.stemType][0], "' fill='#383838' opacity='0.8'>", aniGen(shapes[5][data.stemType], ani), "</path>");
stem = abi.encodePacked(stem, "<path d='", shapes[6][data.faceType][0], "' fill='black'>", aniGen(shapes[6][data.faceType], ani), "</path>");
bytes memory transform = abi.encodePacked("transform='translate()'");
return string(abi.encodePacked("<g ", transform, ">", cap, stem, "</g>"));
}
function frontSvg(MushroomData memory data, bool ani) private view returns (string memory) {
bytes memory svgBox = abi.encodePacked("<rect opacity='0' width='", uint(!ani ? 220 : 880).toString(), "' height='220'/>");
bytes memory svgFront = abi.encodePacked("<path d='", backgrounds[4][data.groundType], "' fill='", colors[0][data.skyColor], "'/>");
svgFront = !ani ? svgFront : abi.encodePacked(svgFront, "<path transform='translate(-440, 0)' d='", backgrounds[4][data.groundType], "' fill='", colors[0][data.skyColor], "'/>");
return string(abi.encodePacked("<g>", svgBox, svgFront, transGen(10, ani), "</g>"));
}
function aniDef(bool ani) private pure returns (string memory) {
if (!ani) return "";
string memory anim;
for (uint i; i < 10; i++) {
string memory begin = i < 1 ? "0;f10" : string(abi.encodePacked("f", i.toString()));
anim = string(abi.encodePacked(anim, "<animate id='f", (i + 1).toString(), "' begin='", begin, ".end' dur='100ms'/>"));
}
return anim;
}
function filterDef() private pure returns (string memory) {
return
string(
abi.encodePacked(
"<filter id='grains'>",
"<feTurbulence seed='12' type='fractalNoise' baseFrequency='15' numOctaves='1' result='turbulence' />",
"<feComponentTransfer width='100%' height='100%' in='turbulence' result='componentTransfer'><feFuncA type='table' tableValues='0 0.35'/></feComponentTransfer>",
"<feBlend in='SourceGraphic' mode='hue'/>",
"</filter>"
)
);
}
function aniGen(string[6] memory values, bool ani) private pure returns (string memory) {
if (!ani) return "";
string memory anim;
uint r = 0;
for (uint i = 1; i <= 10; i++) {
anim = string(abi.encodePacked(anim, "<animate begin='f", i.toString(), ".end' fill='freeze' attributeName='d' dur='300ms' to='", values[i - r], "'/>"));
r = i < 5 ? 0 : r + 2;
}
return anim;
}
function transGen(uint8 dur, bool ani) private pure returns (string memory) {
if (!ani) return "";
return string(abi.encodePacked("<animateTransform begin='0' fill='freeze' attributeName='transform' type='translate' dur='", uint(dur).toString(), "s' from='0 0' to='440 0' repeatCount='indefinite'/>"));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
contract Ownable {
address _owner;
event RenounceOwnership();
constructor() {
_owner = msg.sender;
}
modifier onlyOwner() {
require(_owner == msg.sender, "only owner");
_;
}
function owner() external view virtual returns (address) {
return _owner;
}
function ownerRenounce() public onlyOwner {
_owner = address(0);
emit RenounceOwnership();
}
function transferOwnership(address newOwner) external onlyOwner {
_owner = newOwner;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import "./Erc20.sol";
abstract contract PoolCreatableErc20i is ERC20 {
address internal _pool;
uint256 internal _startTime;
bool internal _feeLocked;
address immutable _pairCreator;
constructor(string memory name_, string memory symbol_, address pairCreator) ERC20(name_, symbol_) {
_pairCreator = pairCreator;
}
modifier lockFee() {
_feeLocked = true;
_;
_feeLocked = false;
}
function launch(address poolAddress) external {
require(msg.sender == _pairCreator);
require(!isStarted());
_pool = poolAddress;
_startTime = block.timestamp;
}
function isStarted() internal view returns (bool) {
return true;
}
function pool() external view returns (address) {
return _pool;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./Erc20.sol";
import "./PoolCreatableErc20i.sol";
import "../Generator.sol";
library ExtraSeedLibrary {
function extra(address account, uint extraSeed) internal pure returns (uint256) {
return uint(keccak256(abi.encodePacked(account, extraSeed)));
}
function seed_data(address account, uint seed, uint extraSeed) internal pure returns (SeedData memory) {
return SeedData(seed, extra(account, extraSeed), account);
}
}
abstract contract Inscriptions is PoolCreatableErc20i {
using ExtraSeedLibrary for address;
mapping(address owner => uint) _counts;
mapping(address owner => mapping(uint index => SeedData seed_data)) _ownedTokens;
mapping(address owner => mapping(uint tokenId => uint)) _ownedTokensIndex;
mapping(address owner => mapping(uint => bool)) _owns;
mapping(address owner => SeedData seed_data) _dynamicInscription;
mapping(uint index => address user) _holderList;
mapping(address user => uint index) _holderListIndexes;
uint _inscriptionsTotalCount;
uint _holdersCount;
uint _dynamicInscriptionTotalCount;
uint _random_nonce;
event OnMushroomTransfer(address indexed from, address indexed to, SeedData seed_data);
event OnSporesGrow(address indexed holder, SeedData seed_data);
event OnSporesShrink(address indexed holder, SeedData seed_data);
constructor() PoolCreatableErc20i("Truffi", "TRUFFI", msg.sender) {}
modifier holder_calculate(address acc1, address acc2) {
bool before1 = _isHolder(acc1);
bool before2 = _isHolder(acc2);
_;
bool after1 = _isHolder(acc1);
bool after2 = _isHolder(acc2);
if (!before1 && after1) _addHolder(acc1);
if (before1 && !after1) _removeHolder(acc1);
if (!before2 && after2) _addHolder(acc2);
if (before2 && !after2) _removeHolder(acc2);
}
function _isHolder(address account) private view returns (bool) {
if (account == address(this) || account == _pool || account == address(0)) return false;
return (_dynamicInscription[account].seed + this.inscriptionCount(account)) > 0;
}
function trySeedTransfer(address from, address to, uint amount) internal holder_calculate(from, to) {
if (from == address(this)) return;
uint seed = amount / (10 ** decimals());
if (seed > 0 && from != _pool && to != _pool) {
// transfer growing inscription
if (_dynamicInscription[from].seed == seed && !_owns[to][seed]) {
SeedData memory data = _dynamicInscription[from];
_removeSeedCount(from, seed);
_addTokenToOwnerEnumeration(to, data);
emit OnMushroomTransfer(from, to, data);
return;
}
// transfer collected inscription
if (_owns[from][seed] && !_owns[to][seed]) {
SeedData memory data = _ownedTokens[from][_ownedTokensIndex[from][seed]];
_removeTokenFromOwnerEnumeration(from, seed);
_addTokenToOwnerEnumeration(to, data);
emit OnMushroomTransfer(from, to, data);
return;
}
}
// transfer dynamicInscription
uint lastBalanceFromSeed = _balances[from] / (10 ** decimals());
uint newBalanceFromSeed = (_balances[from] - amount) / (10 ** decimals());
_removeSeedCount(from, lastBalanceFromSeed - newBalanceFromSeed);
_addSeedCount(to, seed);
}
function _addHolder(address account) private {
_holderList[_holdersCount] = account;
_holderListIndexes[account] = _holdersCount;
++_holdersCount;
}
function _removeHolder(address account) private {
if (_holdersCount == 0) return;
uint removingIndex = _holderListIndexes[account];
if (removingIndex != _holdersCount - 1) {
address lastHolder = _holderList[_holdersCount - 1];
_holderList[removingIndex] = lastHolder;
_holderListIndexes[lastHolder] = removingIndex;
}
--_holdersCount;
delete _holderListIndexes[account];
delete _holderList[_holdersCount];
}
function getHolderByIndex(uint index) public view returns (address) {
return _holderList[index];
}
function getHoldersList(uint startIndex, uint count) public view returns (address[] memory) {
address[] memory holders = new address[](count);
for (uint i = 0; i < count; ++i) holders[i] = getHolderByIndex(startIndex + i);
return holders;
}
function _addSeedCount(address account, uint seed) private {
if (seed == 0) return;
if (account == _pool) return;
SeedData memory last = _dynamicInscription[account];
_dynamicInscription[account].seed += seed;
_dynamicInscription[account].extra = account.extra(++_random_nonce);
_dynamicInscription[account].creator = account;
if (last.seed == 0 && _dynamicInscription[account].seed > 0) ++_dynamicInscriptionTotalCount;
emit OnSporesGrow(account, _dynamicInscription[account]);
}
function _removeSeedCount(address account, uint seed) private {
if (seed == 0) return;
if (account == _pool) return;
SeedData memory lastSpores = _dynamicInscription[account];
if (_dynamicInscription[account].seed >= seed) {
_dynamicInscription[account].seed -= seed;
_dynamicInscription[account].extra = account.extra(++_random_nonce);
_dynamicInscription[account].creator = account;
if (lastSpores.seed > 0 && _dynamicInscription[account].seed == 0) --_dynamicInscriptionTotalCount;
emit OnSporesShrink(account, _dynamicInscription[account]);
return;
}
uint seedRemains = seed - _dynamicInscription[account].seed;
_dynamicInscription[account].seed = 0;
_dynamicInscription[account].extra = account.extra(++_random_nonce);
_dynamicInscription[account].creator = account;
// remove inscriptions
uint count = _counts[account];
uint removed;
for (uint i = 0; i < count && removed < seedRemains; ++i) {
removed += _removeFirstTokenFromOwner(account);
}
if (removed > seedRemains) {
_dynamicInscription[account].seed += removed - seedRemains;
_dynamicInscription[account].extra = account.extra(++_random_nonce);
_dynamicInscription[account].creator = account;
}
if (lastSpores.seed > 0 && _dynamicInscription[account].seed == 0) --_dynamicInscriptionTotalCount;
if (lastSpores.seed == 0 && _dynamicInscription[account].seed > 0) ++_dynamicInscriptionTotalCount;
emit OnSporesShrink(account, _dynamicInscription[account]);
}
function _addTokenToOwnerEnumeration(address to, SeedData memory data) private {
if (to == _pool) return;
++_counts[to];
++_inscriptionsTotalCount;
uint length = _counts[to] - 1;
_ownedTokens[to][length] = data;
_ownedTokensIndex[to][data.seed] = length;
_owns[to][data.seed] = true;
}
function _removeTokenFromOwnerEnumeration(address from, uint seed) private {
if (from == _pool) return;
--_counts[from];
--_inscriptionsTotalCount;
_owns[from][seed] = false;
uint lastTokenIndex = _counts[from];
uint tokenIndex = _ownedTokensIndex[from][seed];
SeedData memory data = _ownedTokens[from][tokenIndex];
// When the token to delete is the last token, the swap operation is unnecessary
if (tokenIndex != lastTokenIndex) {
SeedData memory lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_ownedTokensIndex[from][lastTokenId.seed] = tokenIndex; // Update the moved token's index
}
delete _ownedTokensIndex[from][data.seed];
delete _ownedTokens[from][lastTokenIndex];
}
function _removeFirstTokenFromOwner(address owner) private returns (uint) {
uint count = _counts[owner];
if (count == 0) return 0;
uint seed = _ownedTokens[owner][0].seed;
_removeTokenFromOwnerEnumeration(owner, seed);
return seed;
}
function isOwnerOf(address owner, uint seed) external view returns (bool) {
return _owns[owner][seed];
}
function dynamicInscription(address owner) external view returns (SeedData memory data) {
return _dynamicInscription[owner];
}
function inscriptionCount(address owner) external view returns (uint) {
return _counts[owner];
}
function inscriptionOfOwnerByIndex(address owner, uint index) external view returns (SeedData memory data) {
return _ownedTokens[owner][index];
}
function inscriptionsTotalCount() external view returns (uint) {
return _inscriptionsTotalCount;
}
function holdersCount() external view returns (uint) {
return _holdersCount;
}
function dynamicInscriptionTotalCount() external view returns (uint) {
return _dynamicInscriptionTotalCount;
}
function getHolderIndex(address account) external view returns (uint) {
return _holderListIndexes[account];
}
}
contract Truffi is Inscriptions, Generator, ReentrancyGuard {
uint constant _startTotalSupply = 15e6 * (10 ** _decimals);
uint constant _startMaxBuyCount = (_startTotalSupply * 5) / 10000;
uint constant _addMaxBuyPercentPerSec = 5; // 100%=_addMaxBuyPrecesion add 0.005%/second
uint constant _addMaxBuyPrecesion = 100000;
constructor() {
_mint(msg.sender, _startTotalSupply);
}
modifier maxBuyLimit(uint256 amount) {
require(amount <= maxBuy(), "max buy limit");
_;
}
function maxBuy() public view returns (uint256) {
if (!isStarted()) return _startTotalSupply;
uint256 count = _startMaxBuyCount + (_startTotalSupply * (block.timestamp - _startTime) * _addMaxBuyPercentPerSec) / _addMaxBuyPrecesion;
if (count > _startTotalSupply) count = _startTotalSupply;
return count;
}
function _transfer(address from, address to, uint256 amount) internal override {
if (isStarted()) {
trySeedTransfer(from, to, amount);
} else {
require(from == _owner || to == _owner, "not started");
}
// allow burning
if (to == address(0)) {
_burn(from, amount);
return;
}
// system transfers
if (from == address(this)) {
super._transfer(from, to, amount);
return;
}
if (_feeLocked) {
super._transfer(from, to, amount);
return;
} else {
if (from == _pool) {
buy(to, amount);
return;
}
}
super._transfer(from, to, amount);
}
function buy(address to, uint256 amount) private maxBuyLimit(amount) lockFee {
super._transfer(_pool, to, amount);
}
function burnCount() public view returns (uint256) {
return _startTotalSupply - totalSupply();
}
}
{
"compilationTarget": {
"contracts/token/Truffi.sol": "Truffi"
},
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [],
"viaIR": true
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"},{"internalType":"address","name":"creator","type":"address"}],"indexed":false,"internalType":"struct SeedData","name":"seed_data","type":"tuple"}],"name":"OnMushroomTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holder","type":"address"},{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"},{"internalType":"address","name":"creator","type":"address"}],"indexed":false,"internalType":"struct SeedData","name":"seed_data","type":"tuple"}],"name":"OnSporesGrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holder","type":"address"},{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"},{"internalType":"address","name":"creator","type":"address"}],"indexed":false,"internalType":"struct SeedData","name":"seed_data","type":"tuple"}],"name":"OnSporesShrink","type":"event"},{"anonymous":false,"inputs":[],"name":"RenounceOwnership","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burnCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"dynamicInscription","outputs":[{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"},{"internalType":"address","name":"creator","type":"address"}],"internalType":"struct SeedData","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dynamicInscriptionTotalCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"},{"internalType":"address","name":"creator","type":"address"}],"internalType":"struct SeedData","name":"seed_data","type":"tuple"}],"name":"getAnimatedSvg","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getHolderByIndex","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getHolderIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"getHoldersList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"},{"internalType":"address","name":"creator","type":"address"}],"internalType":"struct SeedData","name":"seed_data","type":"tuple"}],"name":"getMeta","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"},{"internalType":"address","name":"creator","type":"address"}],"internalType":"struct SeedData","name":"seed_data","type":"tuple"}],"name":"getSvg","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"holdersCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"inscriptionCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"inscriptionOfOwnerByIndex","outputs":[{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"},{"internalType":"address","name":"creator","type":"address"}],"internalType":"struct SeedData","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"inscriptionsTotalCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"seed","type":"uint256"}],"name":"isOwnerOf","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"name":"launch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxBuy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownerRenounce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string[5]","name":"data","type":"string[5]"},{"internalType":"uint256","name":"origin","type":"uint256"}],"name":"setBackgrounds","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[10][3]","name":"data","type":"string[10][3]"}],"name":"setColors","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[6][5]","name":"data","type":"string[6][5]"},{"internalType":"uint256","name":"origin","type":"uint256"}],"name":"setMushrooms","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]