// SPDX-License-Identifier: MIT
// Inspired on token.sol from DappHub
pragma solidity ^0.8.0;
import "./IERC20.sol";
contract ERC20 is IERC20 {
uint256 internal _totalSupply;
mapping (address => uint256) internal _balanceOf;
mapping (address => mapping (address => uint256)) internal _allowance;
string public symbol;
uint256 public decimals = 18; // standard token precision. override to customize
string public name = ""; // Optional token name
constructor(string memory name_, string memory symbol_) {
name = name_;
symbol = symbol_;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address guy) public view virtual override returns (uint256) {
return _balanceOf[guy];
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowance[owner][spender];
}
function approve(address spender, uint wad) public virtual override returns (bool) {
return _approve(msg.sender, spender, wad);
}
function transfer(address dst, uint wad) public virtual override returns (bool) {
return _transfer(msg.sender, dst, wad);
}
function transferFrom(address src, address dst, uint wad) public virtual override returns (bool) {
uint256 allowed = _allowance[src][msg.sender];
if (src != msg.sender && allowed != type(uint).max) {
require(allowed >= wad, "ERC20: Insufficient approval");
_approve(src, msg.sender, allowed - wad);
}
return _transfer(src, dst, wad);
}
function _transfer(address src, address dst, uint wad) internal virtual returns (bool) {
require(_balanceOf[src] >= wad, "ERC20: Insufficient balance");
_balanceOf[src] = _balanceOf[src] - wad;
_balanceOf[dst] = _balanceOf[dst] + wad;
emit Transfer(src, dst, wad);
return true;
}
function _approve(address owner, address spender, uint wad) internal virtual returns (bool) {
_allowance[owner][spender] = wad;
emit Approval(owner, spender, wad);
return true;
}
function _mint(address dst, uint wad) internal virtual {
_balanceOf[dst] = _balanceOf[dst] + wad;
_totalSupply = _totalSupply + wad;
emit Transfer(address(0), dst, wad);
}
function _burn(address src, uint wad) internal virtual {
require(_balanceOf[src] >= wad, "ERC20: Insufficient balance");
_balanceOf[src] = _balanceOf[src] - wad;
_totalSupply = _totalSupply - wad;
emit Transfer(src, address(0), wad);
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
// Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/53516bc555a454862470e7860a9b5254db4d00f5/contracts/token/ERC20/ERC20Permit.sol
pragma solidity ^0.8.0;
import "acc-erc20/contracts/ERC20.sol";
import "./IERC2612.sol";
/**
* @author Georgios Konstantopoulos
* @dev Extension of {ERC20} that allows token holders to use their tokens
* without sending any transactions by setting {IERC20-allowance} with a
* signature using the {permit} method, and then spend them via
* {IERC20-transferFrom}.
*
* The {permit} signature mechanism conforms to the {IERC2612} interface.
*/
abstract contract ERC20Permit is ERC20, IERC2612 {
mapping (address => uint256) public override nonces;
bytes32 public immutable PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public immutable DOMAIN_SEPARATOR;
constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {
uint256 chainId;
assembly {
chainId := chainid()
}
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name_)),
keccak256(bytes(version())),
chainId,
address(this)
)
);
}
/// @dev Setting the version as a function so that it can be overriden
function version() public pure virtual returns(string memory) { return "1"; }
/**
* @dev See {IERC2612-permit}.
*
* In cases where the free option is not a concern, deadline can simply be
* set to uint(-1), so it should be seen as an optional parameter
*/
function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual override {
require(deadline >= block.timestamp, "ERC20Permit: expired deadline");
bytes32 hashStruct = keccak256(
abi.encode(
PERMIT_TYPEHASH,
owner,
spender,
amount,
nonces[owner]++,
deadline
)
);
bytes32 hash = keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR,
hashStruct
)
);
address signer = ecrecover(hash, v, r, s);
require(
signer != address(0) && signer == owner,
"ERC20Permit: invalid signature"
);
_approve(owner, spender, amount);
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "erc20permit/contracts/ERC20Permit.sol";
import "./IUSM.sol";
import "./IFUM.sol";
import "./OptOutable.sol";
import "./MinOut.sol";
/**
* @title FUM Token
* @author Alberto Cuesta Cañada, Jacob Eliosoff, Alex Roan
*
* @notice This should be created and owned by the USM instance.
*/
contract FUM is IFUM, ERC20Permit, OptOutable {
IUSM public immutable usm;
constructor(address[] memory addressesYouCantSendThisContractsTokensTo,
address[] memory contractsToAskToRejectSendsToThisContractsAddress)
ERC20Permit("Minimalist USD Funding v1 - Release Candidate 1", "FUM")
OptOutable(addressesYouCantSendThisContractsTokensTo, contractsToAskToRejectSendsToThisContractsAddress)
{
usm = IUSM(msg.sender); // FUM constructor can only be called by a USM instance
}
/**
* @notice If anyone sends ETH here, assume they intend it as a `fund`. If decimals 8 to 11 (inclusive) of the amount of
* ETH received are `0000`, then the next 7 will be parsed as the minimum number of FUM accepted per input ETH, with the
* 7-digit number interpreted as "hundredths of a FUM". See comments in `MinOut`.
*/
receive() external payable {
usm.fund{ value: msg.value }(msg.sender, MinOut.parseMinTokenOut(msg.value));
}
/**
* @notice If a user sends FUM tokens directly to this contract (or to the USM contract), assume they intend it as a
* `defund`. If using `transfer`/`transferFrom` as `defund`, and if decimals 8 to 11 (inclusive) of the amount transferred
* are `0000`, then the next 7 will be parsed as the maximum number of FUM tokens sent per ETH received, with the 7-digit
* number interpreted as "hundredths of a FUM". See comments in `MinOut`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal override noOptOut(recipient) returns (bool)
{
if (recipient == address(this) || recipient == address(usm) || recipient == address(0)) {
usm.defundFrom(sender, payable(sender), amount, MinOut.parseMinEthOut(amount));
} else {
super._transfer(sender, recipient, amount);
}
return true;
}
/**
* @notice Mint new FUM to the recipient
*
* @param recipient address to mint to
* @param amount amount to mint
*/
function mint(address recipient, uint amount) external override {
require(msg.sender == address(usm), "Only USM");
_mint(recipient, amount);
}
/**
* @notice Burn FUM from holder
*
* @param holder address to burn from
* @param amount amount to burn
*/
function burn(address holder, uint amount) external override {
require(msg.sender == address(usm), "Only USM");
_burn(holder, amount);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @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);
}
// SPDX-License-Identifier: GPL-3.0-or-later
// Code adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237/
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC2612 standard as defined in the EIP.
*
* Adds the {permit} method, which can be used to change one's
* {IERC20-allowance} without having to send a transaction, by signing a
* message. This allows users to spend tokens without having to hold Ether.
*
* See https://eips.ethereum.org/EIPS/eip-2612.
*/
interface IERC2612 {
/**
* @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens,
* given `owner`'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
/**
* @dev Returns the current ERC2612 nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "acc-erc20/contracts/IERC20.sol";
interface IFUM is IERC20 {
/**
* @notice Mint new FUM to the recipient
*
* @param recipient address to mint to
* @param amount amount to mint
*/
function mint(address recipient, uint amount) external;
/**
* @notice Burn FUM from holder
*
* @param holder address to burn from
* @param amount amount to burn
*/
function burn(address holder, uint amount) external;
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "acc-erc20/contracts/IERC20.sol";
import "./IFUM.sol";
import "./oracles/Oracle.sol";
interface IUSM is IERC20, Oracle {
event UnderwaterStatusChanged(bool underwater);
event BidAskAdjustmentChanged(uint adjustment);
event PriceChanged(uint price, uint oraclePrice);
enum Side {Buy, Sell}
// ____________________ External transactional functions ____________________
/**
* @notice Mint new USM, sending it to the given address, and only if the amount minted >= `minUsmOut`. The amount of ETH
* is passed in as `msg.value`.
* @param to address to send the USM to.
* @param minUsmOut Minimum accepted USM for a successful mint.
*/
function mint(address to, uint minUsmOut) external payable returns (uint usmOut);
/**
* @dev Burn USM in exchange for ETH.
* @param to address to send the ETH to.
* @param usmToBurn Amount of USM to burn.
* @param minEthOut Minimum accepted ETH for a successful burn.
*/
function burn(address payable to, uint usmToBurn, uint minEthOut) external returns (uint ethOut);
/**
* @notice Funds the pool with ETH, minting new FUM and sending it to the given address, but only if the amount minted >=
* `minFumOut`. The amount of ETH is passed in as `msg.value`.
* @param to address to send the FUM to.
* @param minFumOut Minimum accepted FUM for a successful fund.
*/
function fund(address to, uint minFumOut) external payable returns (uint fumOut);
/**
* @notice Defunds the pool by redeeming FUM in exchange for equivalent ETH from the pool.
* @param to address to send the ETH to.
* @param fumToBurn Amount of FUM to burn.
* @param minEthOut Minimum accepted ETH for a successful defund.
*/
function defund(address payable to, uint fumToBurn, uint minEthOut) external returns (uint ethOut);
/**
* @notice Defunds the pool by redeeming FUM in exchange for equivalent ETH from the pool. Usable only by FUM.
* @param from address to deduct the FUM from.
* @param to address to send the ETH to.
* @param fumToBurn Amount of FUM to burn.
* @param minEthOut Minimum accepted ETH for a successful defund.
*/
function defundFrom(address from, address payable to, uint fumToBurn, uint minEthOut) external returns (uint ethOut);
// ____________________ External informational view functions ____________________
/**
* @return fum_ The FUM instance
*/
function fum() external view returns (IFUM fum_);
/**
* @notice Total amount of ETH in the pool (ie, in the contract).
* @return pool ETH pool
*/
function ethPool() external view returns (uint pool);
/**
* @notice Total amount of ETH in the pool (ie, in the contract).
* @return supply the total supply of FUM. Users of this `IUSM` interface, like `USMView`, need to call this rather than
* `usm.fum().totalSupply()` directly, because `IUSM` doesn't (and shouldn't) know about the `FUM` type.
*/
function fumTotalSupply() external view returns (uint supply);
/**
* @notice The current bid/ask adjustment, equal to the stored value decayed over time towards its stable value, 1. This
* adjustment is intended as a measure of "how long-ETH recent user activity has been", so that we can slide price
* accordingly: if recent activity was mostly long-ETH (`fund()` and `burn()`), raise FUM buy price/reduce USM sell price;
* if recent activity was short-ETH (`defund()` and `mint()`), reduce FUM sell price/raise USM buy price.
* @return adjustment The sliding-price bid/ask adjustment
*/
function bidAskAdjustment() external view returns (uint adjustment);
function timeSystemWentUnderwater() external view returns (uint timestamp);
function isDuringPrefund() external view returns (bool duringPrefund);
// ____________________ External helper pure functions (for functions above) ____________________
/**
* @notice Calculate the amount of ETH in the buffer.
* @return buffer ETH buffer
*/
function ethBuffer(uint ethUsdPrice, uint ethInPool, uint usmSupply, bool roundUp) external pure returns (int buffer);
/**
* @notice Calculate debt ratio for a given eth to USM price: ratio of the outstanding USM (amount of USM in total supply),
* to the current ETH pool value in USD (ETH qty * ETH/USD price).
* @return ratio Debt ratio (or 0 if there's currently 0 ETH in the pool/price = 0: these should never happen after launch)
*/
function debtRatio(uint ethUsdPrice, uint ethInPool, uint usmSupply) external pure returns (uint ratio);
/**
* @notice Convert ETH amount to USM using a ETH/USD price.
* @param ethAmount The amount of ETH to convert
* @return usmOut The amount of USM
*/
function ethToUsm(uint ethUsdPrice, uint ethAmount, bool roundUp) external pure returns (uint usmOut);
/**
* @notice Convert USM amount to ETH using a ETH/USD price.
* @param usmAmount The amount of USM to convert
* @return ethOut The amount of ETH
*/
function usmToEth(uint ethUsdPrice, uint usmAmount, bool roundUp) external pure returns (uint ethOut);
/**
* @return price The ETH/USD price, adjusted by the `bidAskAdjustment` (if applicable) for the given buy/sell side.
*/
function adjustedEthUsdPrice(Side side, uint ethUsdPrice, uint adjustment) external pure returns (uint price);
/**
* @notice Calculate the *marginal* price of USM (in ETH terms): that is, of the next unit, before the price start sliding.
* @return price USM price in ETH terms
*/
function usmPrice(Side side, uint ethUsdPrice) external pure returns (uint price);
/**
* @notice Calculate the *marginal* price of FUM (in ETH terms): that is, of the next unit, before the price starts rising.
* @param usmEffectiveSupply should be either the actual current USM supply, or, when calculating the FUM *buy* price, the
* return value of `usmSupplyForFumBuys()`.
* @return price FUM price in ETH terms
*/
function fumPrice(Side side, uint ethUsdPrice, uint ethInPool, uint usmEffectiveSupply, uint fumSupply, bool prefund) external pure returns (uint price);
/**
* @return timeSystemWentUnderwater_ The time at which we first detected the system was underwater (debt ratio >
* `MAX_DEBT_RATIO`), based on the current oracle price and pool ETH and USM; or 0 if we're not currently underwater.
* @return usmSupplyForFumBuys The current supply of USM *for purposes of calculating the FUM buy price,* and therefore
* for `fumFromFund()`. The "supply for FUM buys" is the *lesser* of the actual current USM supply, and the USM amount
* that would make debt ratio = `MAX_DEBT_RATIO`. Example:
*
* 1. Suppose the system currently contains 50 ETH at price $1,000 (total pool value: $50,000), with an actual USM supply
* of 30,000 USM. Then debt ratio = 30,000 / $50,000 = 60%: < MAX 80%, so `usmSupplyForFumBuys` = 30,000.
* 2. Now suppose ETH/USD halves to $500. Then pool value halves to $25,000, and debt ratio doubles to 120%. Now
* `usmSupplyForFumBuys` instead = 20,000: the USM quantity at which debt ratio would equal 80% (20,000 / $25,000).
* (Call this the "80% supply".)
* 3. ...Except, we also gradually increase the supply over time while we remain underwater. This has the effect of
* *reducing* the FUM buy price inferred from that supply (higher JacobUSM supply -> smaller buffer -> lower FUM price).
* The math we use gradually increases the supply from its initial "80% supply" value, where debt ratio =
* `MAX_DEBT_RATIO` (20,000 above), to a theoretical maximum "100% supply" value, where debt ratio = 100% (in the $500
* example above, this would be 25,000). (Or the actual supply, whichever is lower: we never increase
* `usmSupplyForFumBuys` above `usmActualSupply`.) The climb from the initial 80% supply (20,000) to the 100% supply
* (25,000) is at a rate that brings it "halfway closer per `MIN_FUM_BUY_PRICE_HALF_LIFE` (eg, 1 day)": so three days
* after going underwater, the supply returned will be 25,000 - 0.5**3 * (25,000 - 20,000) = 24,375.
*/
function checkIfUnderwater(uint usmActualSupply, uint ethPool_, uint ethUsdPrice, uint oldTimeUnderwater, uint currentTime) external pure returns (uint timeSystemWentUnderwater_, uint usmSupplyForFumBuys, uint debtRatio_);
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
library MinOut {
uint public constant ZEROES_PLUS_LIMIT_PRICE_DIGITS = 1e11; // 4 digits all 0s, + 7 digits to specify the limit price
uint public constant LIMIT_PRICE_DIGITS = 1e7; // 7 digits to specify the limit price (unscaled)
uint public constant LIMIT_PRICE_SCALING_FACTOR = 100; // So, last 7 digits "1234567" / 100 = limit price 12345.67
function parseMinTokenOut(uint ethIn) internal pure returns (uint minTokenOut) {
uint minPrice;
unchecked { minPrice = ethIn % ZEROES_PLUS_LIMIT_PRICE_DIGITS; }
if (minPrice != 0 && minPrice < LIMIT_PRICE_DIGITS) {
minTokenOut = ethIn * minPrice;
unchecked { minTokenOut /= LIMIT_PRICE_SCALING_FACTOR; }
}
}
function parseMinEthOut(uint tokenIn) internal pure returns (uint minEthOut) {
uint maxPrice;
unchecked { maxPrice = tokenIn % ZEROES_PLUS_LIMIT_PRICE_DIGITS; }
if (maxPrice != 0 && maxPrice < LIMIT_PRICE_DIGITS) {
minEthOut = tokenIn * LIMIT_PRICE_SCALING_FACTOR;
unchecked { minEthOut /= maxPrice; }
}
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
/**
* A general type of contract with a behavior that using contracts can "opt out of". The practical motivation is, when we have
* multiple versions of a token (eg, USMv1 and USMv2), and support "mint/burn via send" - sending ETH/tokens mints/burns tokens
* (respectively), there's a UX risk that users might accidentally send (eg) USMv1 tokens to the USMv2 address: resulting in
* not a burn (returning ETH), but just the tokens being permanently lost with no ETH sent back in exchange.
*
* To avoid this, we want the USMv1 contract to be able to "opt out" of receiving USMv2 tokens, and vice versa:
*
* 1. During creation of USMv2, the address of USMv1 is included in the `addressesNoOptOutFnsShouldFailOn` argument to the
* USMv2 constructor.
* 2. This puts USMv1 in USMv2's `optedOut` state variable.
* 3. Then, if someone accidentally tries to send USMv1 tokens to the USMv2 address, the `_transfer()` call fails, rather
* than the USMv1 tokens being permanently lost.
* 4. And to handle the reverse case, USMv2's constructor can call `USMv1.optOut()`, so that sends of USMv2 tokens to the
* USMv1 address also fail cleanly. (USMv2 couldn't be passed to USMv1's constructor, because USMv2 didn't exist yet!)
*
* See also https://github.com/usmfum/USM/issues/88 and https://github.com/usmfum/USM/pull/93.
*
* Note that this would be prone to abuse if users could call `optOut()` on *other* contracts: so we only let a contract opt
* *itself* out, ie, `optOut()` takes no argument. (Or it could be passed to the constructor of the `OptOutable`-implementing
* contract.)
*/
abstract contract OptOutable {
event OptOutStatusChanged(address indexed user, bool newStatus);
mapping(address => bool) public optedOut; // true = address opted out of certain ops on this contract, marked noOptOut
constructor(address[] memory addressesNoOptOutFnsShouldFailOn, address[] memory contractsToAskToRejectThisContractsAddress)
{
for (uint i = 0; i < addressesNoOptOutFnsShouldFailOn.length; i++) {
optedOut[addressesNoOptOutFnsShouldFailOn[i]] = true;
}
for (uint i = 0; i < contractsToAskToRejectThisContractsAddress.length; i++) {
OptOutable(contractsToAskToRejectThisContractsAddress[i]).optOut();
}
}
modifier noOptOut(address target) {
require(!optedOut[target], "Target opted out");
_;
}
function optOut() public virtual {
if (!optedOut[msg.sender]) {
optedOut[msg.sender] = true;
emit OptOutStatusChanged(msg.sender, true);
}
}
function optBackIn() public virtual {
if (optedOut[msg.sender]) {
optedOut[msg.sender] = false;
emit OptOutStatusChanged(msg.sender, false);
}
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface Oracle {
/**
* @return price WAD-scaled - 18 dec places
*/
function latestPrice() external view returns (uint price);
}
{
"compilationTarget": {
"contracts/FUM.sol": "FUM"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1000000
},
"remappings": []
}
[{"inputs":[{"internalType":"address[]","name":"addressesYouCantSendThisContractsTokensTo","type":"address[]"},{"internalType":"address[]","name":"contractsToAskToRejectSendsToThisContractsAddress","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"newStatus","type":"bool"}],"name":"OptOutStatusChanged","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":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"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":"wad","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"guy","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optBackIn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"optOut","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"optedOut","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"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":"dst","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usm","outputs":[{"internalType":"contract IUSM","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"stateMutability":"payable","type":"receive"}]