// 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;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// - The ERC20 standard allows minting and transferring to and from the zero address,
/// minting and transferring zero tokens, as well as self-approvals.
/// For performance, this implementation WILL NOT revert for such actions.
/// Please add any checks with overrides if desired.
/// - The `permit` function uses the ecrecover precompile (0x1).
///
/// If you are overriding:
/// - NEVER violate the ERC20 invariant:
/// the total sum of all balances must be equal to `totalSupply()`.
/// - Check that the overridden function is actually used in the function you want to
/// change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC20 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The total supply has overflowed.
error TotalSupplyOverflow();
/// @dev The allowance has overflowed.
error AllowanceOverflow();
/// @dev The allowance has underflowed.
error AllowanceUnderflow();
/// @dev Insufficient balance.
error InsufficientBalance();
/// @dev Insufficient allowance.
error InsufficientAllowance();
/// @dev The permit is invalid.
error InvalidPermit();
/// @dev The permit has expired.
error PermitExpired();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
event Transfer(address indexed from, address indexed to, uint256 amount);
/// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
event Approval(address indexed owner, address indexed spender, uint256 amount);
/// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
uint256 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
/// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
uint256 private constant _APPROVAL_EVENT_SIGNATURE =
0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The storage slot for the total supply.
uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;
/// @dev The balance slot of `owner` is given by:
/// ```
/// mstore(0x0c, _BALANCE_SLOT_SEED)
/// mstore(0x00, owner)
/// let balanceSlot := keccak256(0x0c, 0x20)
/// ```
uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;
/// @dev The allowance slot of (`owner`, `spender`) is given by:
/// ```
/// mstore(0x20, spender)
/// mstore(0x0c, _ALLOWANCE_SLOT_SEED)
/// mstore(0x00, owner)
/// let allowanceSlot := keccak256(0x0c, 0x34)
/// ```
uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;
/// @dev The nonce slot of `owner` is given by:
/// ```
/// mstore(0x0c, _NONCES_SLOT_SEED)
/// mstore(0x00, owner)
/// let nonceSlot := keccak256(0x0c, 0x20)
/// ```
uint256 private constant _NONCES_SLOT_SEED = 0x38377508;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 private constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev `keccak256("1")`.
bytes32 private constant _VERSION_HASH =
0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
/// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
bytes32 private constant _PERMIT_TYPEHASH =
0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 METADATA */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the name of the token.
function name() public view virtual returns (string memory);
/// @dev Returns the symbol of the token.
function symbol() public view virtual returns (string memory);
/// @dev Returns the decimals places of the token.
function decimals() public view virtual returns (uint8) {
return 18;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the amount of tokens in existence.
function totalSupply() public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_TOTAL_SUPPLY_SLOT)
}
}
/// @dev Returns the amount of tokens owned by `owner`.
function balanceOf(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
function allowance(address owner, address spender)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x34))
}
}
/// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
///
/// Emits a {Approval} event.
function approve(address spender, uint256 amount) public virtual returns (bool) {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and store the amount.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x34), amount)
// Emit the {Approval} event.
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
}
return true;
}
/// @dev Transfer `amount` tokens from the caller to `to`.
///
/// Requirements:
/// - `from` must at least have `amount`.
///
/// Emits a {Transfer} event.
function transfer(address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(msg.sender, to, amount);
/// @solidity memory-safe-assembly
assembly {
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, caller())
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
}
_afterTokenTransfer(msg.sender, to, amount);
return true;
}
/// @dev Transfers `amount` tokens from `from` to `to`.
///
/// Note: Does not update the allowance if it is the maximum uint256 value.
///
/// Requirements:
/// - `from` must at least have `amount`.
/// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
///
/// Emits a {Transfer} event.
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(from, to, amount);
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
// Compute the allowance slot and load its value.
mstore(0x20, caller())
mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if add(allowance_, 1) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
// Compute the balance slot and load its value.
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
return true;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-2612 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev For more performance, override to return the constant value
/// of `keccak256(bytes(name()))` if `name()` will never change.
function _constantNameHash() internal view virtual returns (bytes32 result) {}
/// @dev Returns the current nonce for `owner`.
/// This value is used to compute the signature for EIP-2612 permit.
function nonces(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
// Compute the nonce slot and load its value.
mstore(0x0c, _NONCES_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
/// authorized by a signed approval by `owner`.
///
/// Emits a {Approval} event.
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
bytes32 nameHash = _constantNameHash();
// We simply calculate it on-the-fly to allow for cases where the `name` may change.
if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
/// @solidity memory-safe-assembly
assembly {
// Revert if the block timestamp is greater than `deadline`.
if gt(timestamp(), deadline) {
mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
revert(0x1c, 0x04)
}
let m := mload(0x40) // Grab the free memory pointer.
// Clean the upper 96 bits.
owner := shr(96, shl(96, owner))
spender := shr(96, shl(96, spender))
// Compute the nonce slot and load its value.
mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
mstore(0x00, owner)
let nonceSlot := keccak256(0x0c, 0x20)
let nonceValue := sload(nonceSlot)
// Prepare the domain separator.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), _VERSION_HASH)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
mstore(0x2e, keccak256(m, 0xa0))
// Prepare the struct hash.
mstore(m, _PERMIT_TYPEHASH)
mstore(add(m, 0x20), owner)
mstore(add(m, 0x40), spender)
mstore(add(m, 0x60), value)
mstore(add(m, 0x80), nonceValue)
mstore(add(m, 0xa0), deadline)
mstore(0x4e, keccak256(m, 0xc0))
// Prepare the ecrecover calldata.
mstore(0x00, keccak256(0x2c, 0x42))
mstore(0x20, and(0xff, v))
mstore(0x40, r)
mstore(0x60, s)
let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20)
// If the ecrecover fails, the returndatasize will be 0x00,
// `owner` will be checked if it equals the hash at 0x00,
// which evaluates to false (i.e. 0), and we will revert.
// If the ecrecover succeeds, the returndatasize will be 0x20,
// `owner` will be compared against the returned address at 0x20.
if iszero(eq(mload(returndatasize()), owner)) {
mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
revert(0x1c, 0x04)
}
// Increment and store the updated nonce.
sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
// Compute the allowance slot and store the value.
// The `owner` is already at slot 0x20.
mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
sstore(keccak256(0x2c, 0x34), value)
// Emit the {Approval} event.
log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
bytes32 nameHash = _constantNameHash();
// We simply calculate it on-the-fly to allow for cases where the `name` may change.
if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Grab the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), _VERSION_HASH)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
result := keccak256(m, 0xa0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL MINT FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Mints `amount` tokens to `to`, increasing the total supply.
///
/// Emits a {Transfer} event.
function _mint(address to, uint256 amount) internal virtual {
_beforeTokenTransfer(address(0), to, amount);
/// @solidity memory-safe-assembly
assembly {
let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
let totalSupplyAfter := add(totalSupplyBefore, amount)
// Revert if the total supply overflows.
if lt(totalSupplyAfter, totalSupplyBefore) {
mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
revert(0x1c, 0x04)
}
// Store the updated total supply.
sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
}
_afterTokenTransfer(address(0), to, amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL BURN FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Burns `amount` tokens from `from`, reducing the total supply.
///
/// Emits a {Transfer} event.
function _burn(address from, uint256 amount) internal virtual {
_beforeTokenTransfer(from, address(0), amount);
/// @solidity memory-safe-assembly
assembly {
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, from)
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Subtract and store the updated total supply.
sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
// Emit the {Transfer} event.
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
}
_afterTokenTransfer(from, address(0), amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL TRANSFER FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Moves `amount` of tokens from `from` to `to`.
function _transfer(address from, address to, uint256 amount) internal virtual {
_beforeTokenTransfer(from, to, amount);
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
// Compute the balance slot and load its value.
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL ALLOWANCE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and load its value.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if add(allowance_, 1) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
}
}
/// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
///
/// Emits a {Approval} event.
function _approve(address owner, address spender, uint256 amount) internal virtual {
/// @solidity memory-safe-assembly
assembly {
let owner_ := shl(96, owner)
// Compute the allowance slot and store the amount.
mstore(0x20, spender)
mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
sstore(keccak256(0x0c, 0x34), amount)
// Emit the {Approval} event.
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HOOKS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Hook that is called before any transfer of tokens.
/// This includes minting and burning.
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/// @dev Hook that is called after any transfer of tokens.
/// This includes minting and burning.
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;
interface IIPShare {
function valueCapture(address subject) external payable;
function createShare(address subject) external payable;
function ipshareCreated(address subject) external returns (bool);
function ipshareBalance(address subject, address holder) external view returns (uint256);
function ipshareSupply(address subject) external view returns (uint256);
function buyShares(address subject, address buyer) external payable returns (uint256);
function sellShares(address subject, uint256 shareAmount) external;
function getPendingProfits(address subject, address staker) external view returns (uint256);
function getMaxStaker(address subject) external view returns (address, uint256);
function getPrice(uint256 supply,uint256 amount) external pure returns (uint256);
function getBuyPrice(address subject, uint256 amount) external view returns (uint256);
function getSellPrice(address subject, uint256 amount) external view returns (uint256);
function getBuyPriceAfterFee(address subject, uint256 amount) external view returns (uint256);
function getSellPriceAfterFee(address subject, uint256 amount) external view returns (uint256);
function getBuyAmountByValue(uint256 supply, uint256 ethAmount) external pure returns (uint256);
error OnlyDonut();
error DonutNotSet();
error PendingTradeNow();
error OnlyStaker();
error FeePercentIsTooLarge();
error TooMuchFee();
error CanntPauseNow();
error CanntUnpauseNow();
error IPShareAlreadyCreated();
error InsufficientPay();
error RefundFail();
error PayCreateFeeFail();
error IPShareNotExist();
error CostTradeFeeFail();
error CanntSellLast10Shares();
error UnableToSendFunds();
error NoFunds();
error InsufficientShares();
error InUnstakingPeriodNow();
error WrongAmountOrInsufficientStakeAmount();
error NoIPShareToRedeem();
error IPShareIsInlockingPeriodNow();
error NoProfitToClaim();
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;
interface IPump {
// errors
error TickHasBeenCreated();
error CantBeZeroAddress();
error CantSetSocialDistributionMoreThanTotalSupply();
error TooMuchFee();
error InsufficientCreateFee();
error PreMineTokenFail();
error RefundFail();
// events
event NewToken(string tick, address indexed token, address indexed creator);
event SocialDistributionContractChanged(address indexed oldContract, address indexed newContract);
event IPShareChanged(address indexed oldIPShare, address indexed newIPShare);
event CreateFeeChanged(uint256 indexed oldFee, uint256 indexed newFee);
event FeeAddressChanged(address indexed oldAddress, address indexed newAddress);
event FeeRatiosChanged(uint256 indexed donutFee, uint256 indexed sellsmanFee);
event ClaimSignerChanged(address indexed oldSigner, address indexed newSigner);
function getIPShare() external view returns (address);
function getFeeReceiver() external view returns (address);
function getFeeRatio() external view returns (uint256[2] memory);
function getClaimFee() external view returns (uint256);
function createToken(string calldata tick) external payable returns (address);
function getClaimSigner() external view returns (address);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;
interface IToken {
error TokenNotListed();
error TokenListed();
error IPShareNotCreated();
error TokenInitialized();
error ClaimOrderExist();
error InvalidSignature();
error InvalidClaimAmount();
error InvalidClaimer();
error OutOfSlippage();
error InsufficientFund();
error RefundFail();
error CostFeeFail();
error CreateDexPoolFail();
error DustIssue();
event ClaimDistributedReward(uint256 indexed timestamp, uint256 indexed amount);
event UserClaimReward(uint256 indexed orderId, address indexed user, uint256 indexed amount);
event Trade(
address indexed buyer,
address indexed sellsman,
bool isBuy,
uint256 tokenAmount,
uint256 ethAmount,
uint256 tiptagFee,
uint256 sellsmanFee
);
event TokenListedToDex(address indexed pair);
function initialize(
address manager_,
address ipshareSubject_,
string memory tick_
) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;
interface IUniswapV2Factory {
function createPair(address tokenA, address tokenB) external returns (address pair);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;
interface IUniswapV2Router02 {
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
function WETH() external pure returns (address);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// 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: UNLICENSED
pragma solidity 0.8.20;
import {ERC20} from "./solady/src/tokens/ERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./interface/IToken.sol";
import "./interface/IIPShare.sol";
import "./interface/IPump.sol";
import "./interface/IUniswapV2Router02.sol";
import "./interface/IUniswapV2Factory.sol";
contract Token is IToken, ERC20, ReentrancyGuard {
string private _name;
string private _symbol;
uint256 private constant secondPerDay = 86400;
uint256 private constant divisor = 10000;
// distribute token total amount
uint256 private constant socialDistributionAmount = 150000000 ether;
uint256 private constant bondingCurveTotalAmount = 700000000 ether;
uint256 private constant liquidityAmount = 150000000 ether;
// social distribution - start util the list event
struct Distribution {
uint256 amount;
uint256 startTime;
uint256 stopTime;
}
Distribution[] private distributionEras;
// last claim to social pool time
uint256 public lastClaimTime;
// pending reward in social pool to claim, init 50m to the bonding curve period
uint256 public pendingClaimSocialRewards = 50000000 ether;
// total claimed reward from social pool
uint256 public totalClaimedSocialRewards;
uint256 public startTime;
mapping(uint256 => bool) public claimedOrder;
// bonding curve
uint256 public bondingCurveSupply;
uint256 private constant priceParam = 11_433_333_333_333_333_333;
// state
address private manager;
address public ipshareSubject;
bool public listed = false;
bool initialized = false;
// dex
address private pair;
address private WETH = 0x4200000000000000000000000000000000000006;
address private uniswapV2Factory = 0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6;
address private uniswapV2Router02 = 0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24;
address private constant BlackHole = 0x000000000000000000000000000000000000dEaD;
// 10 - price: 2.041667e-7
uint256 private constant ethAmountToDex = 10 ether;
receive() external payable {
if (!listed) {
buyToken(0, address(0), 0, address(0));
}
}
function initialize(address manager_, address ipshareSubject_, string memory tick) public override {
if (initialized) {
revert TokenInitialized();
}
initialized = true;
manager = manager_;
ipshareSubject = ipshareSubject_;
_name = tick;
_symbol = tick;
// before dawn of today
startTime = block.timestamp - (block.timestamp % secondPerDay);
lastClaimTime = startTime - 1;
IUniswapV2Factory factory = IUniswapV2Factory(uniswapV2Factory);
pair = factory.createPair(address(this), WETH);
_mint(address(this), socialDistributionAmount + bondingCurveTotalAmount + liquidityAmount);
}
/********************************** social distribution ********************************/
function calculateReward(uint256 from, uint256 to) public view returns (uint256 rewards) {
if (!listed) return 0;
uint256 rewardedTime = from - 1;
if (rewardedTime < startTime) {
rewardedTime = startTime;
}
for (uint8 i = 0; i < distributionEras.length; i++) {
if (rewardedTime > distributionEras[i].stopTime) {
continue;
}
if (to <= distributionEras[i].stopTime) {
rewards += (to - rewardedTime) * distributionEras[i].amount;
} else {
rewards += (distributionEras[i].stopTime - rewardedTime) * distributionEras[i].amount;
}
}
}
function getCurrentDistibutionEra() public view returns (Distribution memory era) {
for (uint8 i = 0; i < distributionEras.length; i++) {
if (block.timestamp >= distributionEras[i].startTime && block.timestamp < distributionEras[i].stopTime) {
return distributionEras[i];
}
}
}
function getCurrentRewardPerDay() public view returns (uint256) {
return getCurrentDistibutionEra().amount * secondPerDay;
}
// set distributed rewards can be claimed by user
function claimPendingSocialRewards() public {
// calculate rewards
uint256 rewards = calculateReward(lastClaimTime, block.timestamp);
if (rewards > 0) {
pendingClaimSocialRewards += rewards;
lastClaimTime = block.timestamp;
emit ClaimDistributedReward(block.timestamp, rewards);
}
}
function userClaim(address token, uint256 orderId, uint256 amount, bytes calldata signature) public payable {
if (!listed) {
revert TokenNotListed();
}
if (claimedOrder[orderId]) {
revert ClaimOrderExist();
}
if (signature.length != 65) {
revert InvalidSignature();
}
if (token != address(this)) {
revert InvalidSignature();
}
uint256 claimFee = IPump(manager).getClaimFee();
if (msg.value < claimFee) {
revert CostFeeFail();
} else if (msg.value > claimFee) {
(bool success, ) = msg.sender.call{value: msg.value - claimFee}("");
if (!success) {
revert RefundFail();
}
} else {
address receiver = IPump(manager).getFeeReceiver();
(bool success, ) = receiver.call{value: claimFee}("");
if (!success) {
revert CostFeeFail();
}
}
bytes32 data = keccak256(abi.encodePacked(token, orderId, msg.sender, amount));
if (!_check(data, signature)) {
revert InvalidSignature();
}
if (pendingClaimSocialRewards < amount) {
claimPendingSocialRewards();
}
if (pendingClaimSocialRewards < amount) {
revert InvalidClaimAmount();
}
pendingClaimSocialRewards -= amount;
totalClaimedSocialRewards += amount;
claimedOrder[orderId] = true;
this.transfer(msg.sender, amount);
emit UserClaimReward(orderId, msg.sender, amount);
}
/********************************** bonding curve ********************************/
function buyToken(
uint256 expectAmount,
address sellsman,
uint16 slippage,
address receiver
) public payable nonReentrant returns (uint256) {
sellsman = _checkBondingCurveState(sellsman);
if (receiver == address(0)) {
receiver = tx.origin;
}
uint256[2] memory feeRatio = IPump(manager).getFeeRatio();
uint256 buyFunds = msg.value;
uint256 tiptagFee = (msg.value * feeRatio[0]) / divisor;
uint256 sellsmanFee = (msg.value * feeRatio[1]) / divisor;
uint256 tokenReceived = _getBuyAmountByValue(buyFunds - tiptagFee - sellsmanFee);
address tiptapFeeAddress = IPump(manager).getFeeReceiver();
if (tokenReceived + bondingCurveSupply >= bondingCurveTotalAmount) {
uint256 actualAmount = bondingCurveTotalAmount - bondingCurveSupply;
// calculate used eth
uint256 usedEth = getBuyPriceAfterFee(actualAmount);
if (usedEth > msg.value) {
revert InsufficientFund();
}
if (usedEth < msg.value) {
// refund
(bool success, ) = msg.sender.call{value: msg.value - usedEth}("");
if (!success) {
revert RefundFail();
}
}
buyFunds = usedEth;
tiptagFee = (usedEth * feeRatio[0]) / divisor;
sellsmanFee = (usedEth * feeRatio[1]) / divisor;
(bool success1, ) = tiptapFeeAddress.call{value: tiptagFee}("");
if (!success1) {
revert CostFeeFail();
}
IIPShare(IPump(manager).getIPShare()).valueCapture{value: sellsmanFee}(sellsman);
this.transfer(receiver, actualAmount);
bondingCurveSupply += actualAmount;
emit Trade(receiver, sellsman, true, actualAmount, usedEth, tiptagFee, sellsmanFee);
// build liquidity pool
_makeLiquidityPool();
listed = true;
return actualAmount;
} else {
if (
slippage > 0 &&
(tokenReceived > (expectAmount * (divisor + slippage)) / divisor ||
tokenReceived < (expectAmount * (divisor - slippage)) / divisor)
) {
revert OutOfSlippage();
}
(bool success, ) = tiptapFeeAddress.call{value: tiptagFee}("");
if (!success) {
revert CostFeeFail();
}
IIPShare(IPump(manager).getIPShare()).valueCapture{value: sellsmanFee}(sellsman);
this.transfer(receiver, tokenReceived);
bondingCurveSupply += tokenReceived;
emit Trade(receiver, sellsman, true, tokenReceived, msg.value, tiptagFee, sellsmanFee);
return tokenReceived;
}
}
function sellToken(uint256 amount, uint256 expectReceive, address sellsman, uint16 slippage) public nonReentrant {
sellsman = _checkBondingCurveState(sellsman);
uint256 sellAmount = amount;
if (balanceOf(msg.sender) < sellAmount) {
sellAmount = balanceOf(msg.sender);
}
if (sellAmount < 100000000) {
revert DustIssue();
}
uint256 afterSupply = 0;
afterSupply = bondingCurveSupply - sellAmount;
uint256 price = getPrice(afterSupply, sellAmount);
uint256[2] memory feeRatio = IPump(manager).getFeeRatio();
address tiptagFeeAddress = IPump(manager).getFeeReceiver();
uint256 tiptagFee = (price * feeRatio[0]) / divisor;
uint256 sellsmanFee = (price * feeRatio[1]) / divisor;
uint256 receivedEth = price - tiptagFee - sellsmanFee;
if (
expectReceive > 0 &&
slippage > 0 &&
(receivedEth > ((divisor + slippage) * expectReceive) / divisor ||
receivedEth < ((divisor - slippage) * expectReceive) / divisor)
) {
revert OutOfSlippage();
}
transfer(address(this), sellAmount);
{
(bool success1, ) = tiptagFeeAddress.call{value: tiptagFee}("");
(bool success2, ) = msg.sender.call{value: receivedEth}("");
if (!success1 || !success2) {
revert RefundFail();
}
}
IIPShare(IPump(manager).getIPShare()).valueCapture{value: sellsmanFee}(sellsman);
bondingCurveSupply -= sellAmount;
emit Trade(msg.sender, sellsman, false, sellAmount, price, tiptagFee, sellsmanFee);
}
function _checkBondingCurveState(address sellsman) private returns (address) {
if (listed) {
revert TokenListed();
}
if (sellsman == address(0)) {
sellsman = ipshareSubject;
} else if (!IIPShare(IPump(manager).getIPShare()).ipshareCreated(sellsman)) {
revert IPShareNotCreated();
}
return sellsman;
}
/**
* calculate the eth price when user buy amount tokens
*/
function getPrice(uint256 supply, uint256 amount) public pure returns (uint256) {
supply = supply / 100;
amount = amount / 100;
uint256 price = amount * (amount ** 2 + 3 * amount * supply + 3 * (supply ** 2));
return price / priceParam / 3e36;
}
function getBuyPrice(uint256 amount) public view returns (uint256) {
return getPrice(bondingCurveSupply, amount);
}
function getSellPrice(uint256 amount) public view returns (uint256) {
return getPrice(bondingCurveSupply - amount, amount);
}
function getBuyPriceAfterFee(uint256 amount) public view returns (uint256) {
uint256 price = getBuyPrice(amount);
uint256[2] memory feeRatio = IPump(manager).getFeeRatio();
return ((price * divisor) / (divisor - feeRatio[0] - feeRatio[1]));
}
function getSellPriceAfterFee(uint256 amount) public view returns (uint256) {
uint256 price = getSellPrice(amount);
uint256[2] memory feeRatio = IPump(manager).getFeeRatio();
return (price * (divisor - feeRatio[0] - feeRatio[1])) / divisor;
}
function _getBuyAmountByValue(uint256 ethAmount) private view returns (uint256) {
return (floorCbrt(ethAmount * priceParam * 3e36 +
(bondingCurveSupply / 100) ** 3) - (bondingCurveSupply / 100))
* 100;
}
function getBuyAmountByValue(uint256 ethAmount) public view returns (uint256) {
uint256 amount = _getBuyAmountByValue(ethAmount);
if (amount + bondingCurveSupply > bondingCurveTotalAmount) {
return bondingCurveTotalAmount - bondingCurveSupply;
}
return amount;
}
function getETHAmountToDex() public view returns (uint256) {
return getBuyPriceAfterFee(bondingCurveTotalAmount - bondingCurveSupply);
}
function floorCbrt(uint256 n) internal pure returns (uint256) {
unchecked {
uint256 x = 0;
for (uint256 y = 1 << 255; y > 0; y >>= 3) {
x <<= 1;
uint256 z = 3 * x * (x + 1) + 1;
if (n / y >= z) {
n -= y * z;
x += 1;
}
}
return x;
}
}
/********************************** to dex ********************************/
function _makeLiquidityPool() private {
_approve(address(this), uniswapV2Router02, liquidityAmount);
// v2
IUniswapV2Router02 router = IUniswapV2Router02(uniswapV2Router02);
router.addLiquidityETH{
value: address(this).balance
}(address(this), liquidityAmount, 0, 0, BlackHole, block.timestamp + 300);
startTime = block.timestamp - (block.timestamp % secondPerDay);
lastClaimTime = startTime - 1;
distributionEras.push(
Distribution({amount: 11574074074074074000, startTime: startTime, stopTime: startTime + 100 * 86400})
);
emit TokenListedToDex(pair);
// v3
// create pool
// address pool = INonfungiblePositionManager(positionManager).createAndInitializePoolIfNecessary(
// address(this),
// WETH,
// 500,
// sqrtPrice
// );
// if (pool == address(0)) {
// revert CreateDexPoolFail();
// }
// INonfungiblePositionManager.MintParams memory params
// = INonfungiblePositionManager.MintParams({
// token0: address(this),
// token1: WETH,
// fee: 500,
// tickLower: -887220,
// tickUpper: 887220,
// amount0Desired: liquidityAmount,
// amount1Desired: ethAmountToDex,
// amount0Min: 0,
// amount1Min: 0,
// recipient: BlackHole,
// deadline: block.timestamp
// });
// // // add liquidity
// INonfungiblePositionManager(positionManager).mint{
// value: ethAmountToDex
// }(
// params
// );
}
/********************************** erc20 function ********************************/
function name() public view override returns (string memory) {
return _name;
}
function symbol() public view override returns (string memory) {
return _symbol;
}
// only listed token can do erc20 transfer functions
function _beforeTokenTransfer(address from, address to, uint256 amount) internal override {
if (!listed && to == pair && from != address(this)) {
revert TokenNotListed();
}
return super._beforeTokenTransfer(from, to, amount);
}
function _check(bytes32 data, bytes calldata sign) internal view returns (bool) {
bytes32 r = abi.decode(sign[:32], (bytes32));
bytes32 s = abi.decode(sign[32:64], (bytes32));
uint8 v = uint8(sign[64]);
if (v < 27) {
if (v == 0 || v == 1) v += 27;
}
bytes memory profix = "\x19Ethereum Signed Message:\n32";
bytes32 info = keccak256(abi.encodePacked(profix, data));
address addr = ecrecover(info, v, r, s);
return addr == IPump(manager).getClaimSigner();
}
}
{
"compilationTarget": {
"contracts/Token.sol": "Token"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[],"name":"AllowanceOverflow","type":"error"},{"inputs":[],"name":"AllowanceUnderflow","type":"error"},{"inputs":[],"name":"ClaimOrderExist","type":"error"},{"inputs":[],"name":"CostFeeFail","type":"error"},{"inputs":[],"name":"CreateDexPoolFail","type":"error"},{"inputs":[],"name":"DustIssue","type":"error"},{"inputs":[],"name":"IPShareNotCreated","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientFund","type":"error"},{"inputs":[],"name":"InvalidClaimAmount","type":"error"},{"inputs":[],"name":"InvalidClaimer","type":"error"},{"inputs":[],"name":"InvalidPermit","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"OutOfSlippage","type":"error"},{"inputs":[],"name":"PermitExpired","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"RefundFail","type":"error"},{"inputs":[],"name":"TokenInitialized","type":"error"},{"inputs":[],"name":"TokenListed","type":"error"},{"inputs":[],"name":"TokenNotListed","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","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":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ClaimDistributedReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pair","type":"address"}],"name":"TokenListedToDex","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"address","name":"sellsman","type":"address"},{"indexed":false,"internalType":"bool","name":"isBuy","type":"bool"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tiptagFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellsmanFee","type":"uint256"}],"name":"Trade","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":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"orderId","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UserClaimReward","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"result","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":"result","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":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bondingCurveSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"expectAmount","type":"uint256"},{"internalType":"address","name":"sellsman","type":"address"},{"internalType":"uint16","name":"slippage","type":"uint16"},{"internalType":"address","name":"receiver","type":"address"}],"name":"buyToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"from","type":"uint256"},{"internalType":"uint256","name":"to","type":"uint256"}],"name":"calculateReward","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimPendingSocialRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"claimedOrder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ethAmount","type":"uint256"}],"name":"getBuyAmountByValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getBuyPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getBuyPriceAfterFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentDistibutionEra","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"stopTime","type":"uint256"}],"internalType":"struct Token.Distribution","name":"era","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentRewardPerDay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getETHAmountToDex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"supply","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getSellPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getSellPriceAfterFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"manager_","type":"address"},{"internalType":"address","name":"ipshareSubject_","type":"address"},{"internalType":"string","name":"tick","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ipshareSubject","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastClaimTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"listed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingClaimSocialRewards","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":"value","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":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expectReceive","type":"uint256"},{"internalType":"address","name":"sellsman","type":"address"},{"internalType":"uint16","name":"slippage","type":"uint16"}],"name":"sellToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalClaimedSocialRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"result","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":"token","type":"address"},{"internalType":"uint256","name":"orderId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"userClaim","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]