// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;
import {Authority} from "./Authority.sol";
/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Modified by BadgerDAO to remove owner
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
contract AuthNoOwner {
event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
Authority private _authority;
bool private _authorityInitialized;
modifier requiresAuth() virtual {
require(isAuthorized(msg.sender, msg.sig), "Auth: UNAUTHORIZED");
_;
}
function authority() public view returns (Authority) {
return _authority;
}
function authorityInitialized() public view returns (bool) {
return _authorityInitialized;
}
function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
Authority auth = _authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
// Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
// aware that this makes protected functions uncallable even to the owner if the authority is out of order.
return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig));
}
/// @notice Changed constructor to initialize to allow flexiblity of constructor vs initializer use
/// @notice sets authorityInitiailzed flag to ensure only one use of
function _initializeAuthority(address newAuthority) internal {
require(address(_authority) == address(0), "Auth: authority is non-zero");
require(!_authorityInitialized, "Auth: authority already initialized");
_authority = Authority(newAuthority);
_authorityInitialized = true;
emit AuthorityUpdated(address(this), Authority(newAuthority));
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;
/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
function canCall(address user, address target, bytes4 functionSig) external view returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "./Interfaces/IEBTCToken.sol";
import "./Dependencies/AuthNoOwner.sol";
import "./Dependencies/PermitNonce.sol";
/*
*
* Based upon OpenZeppelin's ERC20 contract:
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol
*
* and their EIP2612 (ERC20Permit / ERC712) functionality:
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/53516bc555a454862470e7860a9b5254db4d00f5/contracts/token/ERC20/ERC20Permit.sol
*
*
* --- Functionality added specific to the EBTCToken ---
*
* 1) Transfer protection: blocklist of addresses that are invalid recipients (i.e. core Ebtc contracts) in external transfer() and transferFrom() calls.
* The purpose is to protect users from losing tokens by mistakenly sending EBTC directly to a Liquity.
* core contract, when they should rather call the right function.
*/
contract EBTCToken is IEBTCToken, AuthNoOwner, PermitNonce {
uint256 private _totalSupply;
string internal constant _NAME = "eBTC";
string internal constant _SYMBOL = "EBTC";
string internal constant _VERSION = "1";
uint8 internal constant _DECIMALS = 18;
// --- Data for EIP2612 ---
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 private constant _PERMIT_TYPEHASH =
0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
// keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
bytes32 private constant _TYPE_HASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
uint256 private immutable _CACHED_CHAIN_ID;
bytes32 private immutable _HASHED_NAME;
bytes32 private immutable _HASHED_VERSION;
// User data for EBTC token
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
// --- Addresses ---
address public immutable cdpManagerAddress;
address public immutable borrowerOperationsAddress;
/// @param _cdpManagerAddress Address of the CDP Manager
/// @param _borrowerOperationsAddress Address of the Borrower Operations
/// @param _authorityAddress Address of the authority for the contract
constructor(
address _cdpManagerAddress,
address _borrowerOperationsAddress,
address _authorityAddress
) {
_initializeAuthority(_authorityAddress);
cdpManagerAddress = _cdpManagerAddress;
borrowerOperationsAddress = _borrowerOperationsAddress;
bytes32 hashedName = keccak256(bytes(_NAME));
bytes32 hashedVersion = keccak256(bytes(_VERSION));
_HASHED_NAME = hashedName;
_HASHED_VERSION = hashedVersion;
_CACHED_CHAIN_ID = _chainID();
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(_TYPE_HASH, hashedName, hashedVersion);
}
/// @notice Mint new tokens
/// @dev Internal system function - only callable by BorrowerOperations or CDPManager
/// @dev Governance can also expand the list of approved minters to enable other systems to mint tokens
/// @param _account The address to receive the newly minted tokens
/// @param _amount The amount of tokens to mint
function mint(address _account, uint256 _amount) external override {
_requireCallerIsBOorCdpMOrAuth();
_mint(_account, _amount);
}
/// @notice Burn existing tokens
/// @dev Internal system function - only callable by BorrowerOperations or CDPManager
/// @dev Governance can also expand the list of approved burners to enable other systems to burn tokens
/// @param _account The address to burn tokens from
/// @param _amount The amount of tokens to burn
function burn(address _account, uint256 _amount) external override {
_requireCallerIsBOorCdpMOrAuth();
_burn(_account, _amount);
}
/// @notice Burn existing tokens from caller
/// @dev Internal system function - only callable by BorrowerOperations or CDPManager
/// @dev Governance can also expand the list of approved burners to enable other systems to burn tokens
/// @param _amount The amount of tokens to burn
function burn(uint256 _amount) external {
_requireCallerIsBOorCdpMOrAuth();
_burn(msg.sender, _amount);
}
// --- External functions ---
function totalSupply() external view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view override returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) external override returns (bool) {
_requireValidRecipient(recipient);
_transfer(msg.sender, recipient, amount);
return true;
}
function allowance(address owner, address spender) external view override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) external override returns (bool) {
_approve(msg.sender, spender, amount);
return true;
}
function transferFrom(
address sender,
address recipient,
uint256 amount
) external override returns (bool) {
_requireValidRecipient(recipient);
_transfer(sender, recipient, amount);
uint256 cachedAllowance = _allowances[sender][msg.sender];
if (cachedAllowance != type(uint256).max) {
require(cachedAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, msg.sender, cachedAllowance - amount);
}
}
return true;
}
function increaseAllowance(
address spender,
uint256 addedValue
) external override returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender] + addedValue);
return true;
}
function decreaseAllowance(
address spender,
uint256 subtractedValue
) external override returns (bool) {
uint256 cachedAllowances = _allowances[msg.sender][spender];
require(cachedAllowances >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(msg.sender, spender, cachedAllowances - subtractedValue);
}
return true;
}
// --- EIP 2612 Functionality (https://eips.ethereum.org/EIPS/eip-2612) ---
/// @notice This function returns the domain separator for current chain
/// @return EIP712 compatible Domain definition
function DOMAIN_SEPARATOR() external view returns (bytes32) {
return domainSeparator();
}
/// @notice This function returns the domain separator for current chain
/// @return EIP712 compatible Domain definition
function domainSeparator() public view override returns (bytes32) {
if (_chainID() == _CACHED_CHAIN_ID) {
return _CACHED_DOMAIN_SEPARATOR;
} else {
return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
}
}
/// @notice This function approve given amount for specified owner and spender
/// @notice by verifying the validity of given deadline and signature parameters (v, r, s).
/// @param owner The token owner
/// @param spender The consumer to which owner want to grant approval
/// @param amount The token expenditure budget to be set
/// @param deadline The permit valid deadline
/// @param v The v part of signature from owner
/// @param r The r part of signature from owner
/// @param s The s part of signature from owner
function permit(
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external override {
require(deadline >= block.timestamp, "EBTC: expired deadline");
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
domainSeparator(),
keccak256(
abi.encode(_PERMIT_TYPEHASH, owner, spender, amount, _nonces[owner]++, deadline)
)
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress == owner, "EBTC: invalid signature");
_approve(owner, spender, amount);
}
/// @dev Return current nonce for specified owner fOR EIP-2612 compatibility
/// @param owner The address whose nonce to be queried
function nonces(address owner) external view override(IERC2612, PermitNonce) returns (uint256) {
return _nonces[owner];
}
// --- Internal operations ---
function _chainID() private view returns (uint256) {
return block.chainid;
}
function _buildDomainSeparator(
bytes32 typeHash,
bytes32 name,
bytes32 version
) private view returns (bytes32) {
return keccak256(abi.encode(typeHash, name, version, _chainID(), address(this)));
}
// --- Internal operations ---
// Warning: sanity checks (for sender and recipient) should have been done before calling these internal functions
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "EBTCToken: zero sender!");
require(recipient != address(0), "EBTCToken: zero recipient!");
uint256 cachedSenderBalances = _balances[sender];
require(cachedSenderBalances >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
// Safe because of the check above
_balances[sender] = cachedSenderBalances - amount;
}
_balances[recipient] = _balances[recipient] + amount;
emit Transfer(sender, recipient, amount);
}
function _mint(address account, uint256 amount) internal {
require(account != address(0), "EBTCToken: mint to zero recipient!");
_totalSupply = _totalSupply + amount;
_balances[account] = _balances[account] + amount;
emit Transfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal {
require(account != address(0), "EBTCToken: burn from zero account!");
uint256 cachedBalance = _balances[account];
require(cachedBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
// Safe because of the check above
_balances[account] = cachedBalance - amount;
}
_totalSupply = _totalSupply - amount;
emit Transfer(account, address(0), amount);
}
function _approve(address owner, address spender, uint256 amount) internal {
require(owner != address(0), "EBTCToken: zero approve owner!");
require(spender != address(0), "EBTCToken: zero approve spender!");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
// --- 'require' functions ---
function _requireValidRecipient(address _recipient) internal view {
require(
_recipient != address(0) && _recipient != address(this),
"EBTC: Cannot transfer tokens directly to the EBTC token contract or the zero address"
);
require(
_recipient != cdpManagerAddress && _recipient != borrowerOperationsAddress,
"EBTC: Cannot transfer tokens directly to the CdpManager or BorrowerOps"
);
}
function _requireCallerIsBorrowerOperations() internal view {
require(
msg.sender == borrowerOperationsAddress,
"EBTCToken: Caller is not BorrowerOperations"
);
}
/// @dev authority check last to short-circuit in the case of use by usual immutable addresses
function _requireCallerIsBOorCdpMOrAuth() internal view {
require(
msg.sender == borrowerOperationsAddress ||
msg.sender == cdpManagerAddress ||
isAuthorized(msg.sender, msg.sig),
"EBTC: Caller is neither BorrowerOperations nor CdpManager nor authorized"
);
}
function _requireCallerIsCdpM() internal view {
require(msg.sender == cdpManagerAddress, "EBTC: Caller is not CdpManager");
}
// --- Optional functions ---
/// @notice Returns the name of the token
/// @return Name of the token
function name() external pure override returns (string memory) {
return _NAME;
}
/// @notice Returns the symbol of the token
/// @return Symbol of the token
function symbol() external pure override returns (string memory) {
return _SYMBOL;
}
/// @notice Returns the number of decimals used to represent token amounts
/// @return Number of decimals used by the token
function decimals() external pure override returns (uint8) {
return _DECIMALS;
}
/// @notice Returns the version of the token
/// @return Version of the token
function version() external pure override returns (string memory) {
return _VERSION;
}
/// @notice Returns the type hash used for permit() function as per EIP-2612
/// @return EIP-2612 permit type hash
function permitTypeHash() external pure override returns (bytes32) {
return _PERMIT_TYPEHASH;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "../Dependencies/IERC20.sol";
import "../Dependencies/IERC2612.sol";
interface IEBTCToken is IERC20, IERC2612 {
// --- Functions ---
function mint(address _account, uint256 _amount) external;
function burn(address _account, uint256 _amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
/**
* Based on the OpenZeppelin IER20 interface:
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol
*
* @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);
function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
/**
* @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);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
/**
* @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: MIT
pragma solidity 0.8.17;
/**
* @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.
*
* Code adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237/
*/
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.
*
* `owner` can limit the time a Permit is valid for by setting `deadline` to
* a value in the near future. The deadline argument can be set to uint256(-1) to
* create Permits that effectively never expire.
*/
function nonces(address owner) external view returns (uint256);
function version() external view returns (string memory);
function permitTypeHash() external view returns (bytes32);
function domainSeparator() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
interface IPermitNonce {
// --- Functions ---
function increasePermitNonce() external returns (uint256);
function nonces(address owner) external view returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "../Interfaces/IPermitNonce.sol";
/**
* @dev This abstract contract provides a mapping from address to nonce (uint256) used for permit signature
*/
contract PermitNonce is IPermitNonce {
mapping(address => uint256) internal _nonces;
/// @dev Increase current nonce for msg.sender by one.
/// @notice This function could be used to invalidate any signed permit out there
function increasePermitNonce() external returns (uint256) {
return ++_nonces[msg.sender];
}
/// @dev Return current nonce for msg.sender fOR EIP-2612 compatibility
function nonces(address owner) external view virtual returns (uint256) {
return _nonces[owner];
}
}
{
"compilationTarget": {
"contracts/EBTCToken.sol": "EBTCToken"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_cdpManagerAddress","type":"address"},{"internalType":"address","name":"_borrowerOperationsAddress","type":"address"},{"internalType":"address","name":"_authorityAddress","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":true,"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"AuthorityUpdated","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":[{"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":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authorityInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerOperationsAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cdpManagerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"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":[],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"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":[],"name":"increasePermitNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","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":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"permitTypeHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"}]