编译器
0.8.19+commit.7dd6d404
文件 1 的 7:Adapter.sol
pragma solidity =0.8.19;
import "./AdapterHelper.sol";
contract Adapter is AdapterHelper {
constructor(
address _tokenProfitAddress,
address _uniV2RouterAddress,
address _liquidNFTsRouterAddress,
address _liquidNFTsWETHPool,
address _liquidNFTsUSDCPool
)
AdapterDeclarations(
_tokenProfitAddress,
_uniV2RouterAddress,
_liquidNFTsRouterAddress,
_liquidNFTsWETHPool,
_liquidNFTsUSDCPool
)
{
admin = tx.origin;
multisig = tx.origin;
}
function syncServices()
external
{
liquidNFTsUSDCPool.manualSyncPool();
liquidNFTsWETHPool.manualSyncPool();
}
function swapUSDCForETH(
uint256 _amount,
uint256 _minAmountOut
)
external
onlyMultiSig
returns (
uint256 amountIn,
uint256 amountOut
)
{
address[] memory path = new address[](2);
path[0] = USDC_ADDRESS;
path[1] = WETH_ADDRESS;
uint256[] memory amounts = _executeSwap(
path,
_amount,
_minAmountOut
);
emit AdminSwap(
USDC_ADDRESS,
WETH_ADDRESS,
amounts[0],
amounts[1]
);
return (
amounts[0],
amounts[1]
);
}
function _precisionWithFee()
internal
view
returns (uint256)
{
return FEE_PRECISION + buyFee;
}
function getTokenAmountFromEthAmount(
uint256 _ethAmount
)
external
view
returns (uint256)
{
return tokenProfit.totalSupply()
* _ethAmount
* FEE_PRECISION
/ _precisionWithFee()
/ _calculateTotalEthValue(
0
);
}
function getEthAmountFromTokenAmount(
uint256 _tokenAmount,
uint256 _msgValue
)
external
view
returns (uint256)
{
return _calculateTotalEthValue(
_msgValue
)
* _tokenAmount
* PRECISION_FACTOR
* _precisionWithFee()
/ FEE_PRECISION
/ tokenProfit.totalSupply()
/ PRECISION_FACTOR;
}
function proposeNewMultisig(
address _proposedMultisig
)
external
onlyMultiSig
{
address oldProposedMultisig = proposedMultisig;
proposedMultisig = _proposedMultisig;
emit MultisigUpdateProposed(
oldProposedMultisig,
_proposedMultisig
);
}
function claimMultisigOwnership()
external
onlyProposedMultisig
{
address oldMultisig = multisig;
multisig = proposedMultisig;
proposedMultisig = ZERO_ADDRESS;
emit MultisigUpdate(
oldMultisig,
multisig
);
}
function proposeNewAdmin(
address _newProposedAdmin
)
external
onlyMultiSig
{
address oldProposedAdmin = proposedAdmin;
proposedAdmin = _newProposedAdmin;
emit AdminUpdateProposed(
oldProposedAdmin,
_newProposedAdmin
);
}
function claimAdminOwnership()
external
onlyProposedAdmin
{
address oldAdmin = admin;
admin = proposedAdmin;
proposedAdmin = ZERO_ADDRESS;
emit AdminUpdate(
oldAdmin,
admin
);
}
function changeBuyFee(
uint256 _newFeeValue
)
external
onlyMultiSig
{
require(
FEE_THRESHOLD > _newFeeValue,
"Adapter: FEE_TOO_HIGH"
);
require(
_newFeeValue > FEE_LOWER_BOUND,
"Adapter: FEE_TOO_LOW"
);
uint256 oldValue = buyFee;
buyFee = _newFeeValue;
emit BuyFeeChanged(
oldValue,
_newFeeValue
);
}
function swapETHforUSDC(
uint256 _amount,
uint256 _minAmountOut
)
external
onlyMultiSig
returns (
uint256 amountIn,
uint256 amountOut
)
{
address[] memory path = new address[](2);
path[0] = WETH_ADDRESS;
path[1] = USDC_ADDRESS;
uint256[] memory amounts = _executeSwapWithValue(
path,
_amount,
_minAmountOut
);
emit AdminSwap(
WETH_ADDRESS,
USDC_ADDRESS,
amounts[0],
amounts[1]
);
return (
amounts[0],
amounts[1]
);
}
function depositETHLiquidNFTs(
uint256 _amount
)
external
onlyAdmin
{
_wrapETH(
_amount
);
_depositLiquidNFTsWrapper(
liquidNFTsWETHPool,
_amount
);
}
function depositUSDCLiquidNFTs(
uint256 _amount
)
external
onlyAdmin
{
_depositLiquidNFTsWrapper(
liquidNFTsUSDCPool,
_amount
);
}
function withdrawUSDCLiquidNFTs(
uint256 _amount
)
external
onlyAdmin
{
_withdrawLiquidNFTsWrapper(
liquidNFTsUSDCPool,
_amount
);
}
function withdrawETHLiquidNFTs(
uint256 _amount
)
external
onlyAdmin
{
_withdrawLiquidNFTsWrapper(
liquidNFTsWETHPool,
_amount
);
_unwrapETH(
WETH.balanceOf(
TOKEN_PROFIT_ADDRESS
)
);
}
function assistWithdrawTokens(
uint256 _index,
uint256 _amount
)
external
onlyTokenProfit
returns (uint256)
{
if (tokens[_index].tokenERC20 == USDC) {
return _USDCRoutine(
_amount
);
}
revert InvalidToken();
}
function assistWithdrawETH(
uint256 _amount
)
external
onlyTokenProfit
returns (uint256)
{
return _WETHRoutine(
_amount
);
}
}
文件 2 的 7:AdapterDeclarations.sol
pragma solidity =0.8.19;
import "./AdapterInterfaces.sol";
error InvalidFeed();
error InvalidToken();
error InvalidDecimals();
contract AdapterDeclarations {
struct TokenData {
IERC20 tokenERC20;
IChainLink feedLink;
uint8 feedDecimals;
uint8 tokenDecimals;
}
uint256 constant TOKENS = 1;
TokenData[TOKENS] public tokens;
IERC20 public immutable WETH;
IERC20 public immutable USDC;
address public immutable WETH_ADDRESS;
address public immutable USDC_ADDRESS;
address public immutable TOKEN_PROFIT_ADDRESS;
address public immutable UNIV2_ROUTER_ADDRESS;
address public immutable LIQUID_NFT_ROUTER_ADDRESS;
ITokenProfit public immutable tokenProfit;
ILiquidNFTsPool public immutable liquidNFTsWETHPool;
ILiquidNFTsPool public immutable liquidNFTsUSDCPool;
address public admin;
address public multisig;
address public proposedMultisig;
address public proposedAdmin;
uint256 public buyFee = 1000;
uint256 constant public FEE_PRECISION = 1E4;
uint256 constant public FEE_THRESHOLD = 50000;
uint256 constant public FEE_LOWER_BOUND = 10;
uint256 constant public PRECISION_FACTOR = 1E18;
uint80 constant MAX_ROUND_COUNT = 50;
address constant ZERO_ADDRESS = address(0x0);
uint256 constant UINT256_MAX = type(uint256).max;
mapping(address => uint256) public chainLinkHeartBeat;
modifier onlyAdmin() {
require(
msg.sender == admin,
"AdapterDeclarations: NOT_ADMIN"
);
_;
}
modifier onlyTokenProfit() {
require(
msg.sender == TOKEN_PROFIT_ADDRESS,
"AdapterDeclarations: NOT_TOKEN_PROFIT"
);
_;
}
modifier onlyProposedMultisig() {
require(
msg.sender == proposedMultisig,
"AdapterDeclarations: NOT_PROPOSED_MULTISIG"
);
_;
}
modifier onlyProposedAdmin() {
require(
msg.sender == proposedAdmin,
"AdapterDeclarations: NOT_PROPOSED_ADMIN"
);
_;
}
modifier onlyMultiSig() {
require(
msg.sender == multisig,
"AdapterDeclarations: NOT_MULTISIG"
);
_;
}
event AdminSwap(
address indexed from,
address indexed to,
uint256 amountIn,
uint256 amountOut
);
event BuyFeeChanged(
uint256 indexed oldFee,
uint256 indexed newFee
);
event MultisigUpdate(
address oldMultisig,
address newMultisig
);
event MultisigUpdateProposed(
address oldProposedMultisig,
address newProposedMultisig
);
event AdminUpdate(
address oldAdmin,
address newAdmin
);
event AdminUpdateProposed(
address oldProposedAdmin,
address newProposedAdmin
);
constructor(
address _tokenProfitAddress,
address _uniV2RouterAddress,
address _liquidNFTsRouterAddress,
address _liquidNFTsWETHPool,
address _liquidNFTsUSDCPool
) {
liquidNFTsWETHPool = ILiquidNFTsPool(
_liquidNFTsWETHPool
);
liquidNFTsUSDCPool = ILiquidNFTsPool(
_liquidNFTsUSDCPool
);
LIQUID_NFT_ROUTER_ADDRESS = _liquidNFTsRouterAddress;
USDC_ADDRESS = liquidNFTsUSDCPool.poolToken();
WETH_ADDRESS = liquidNFTsWETHPool.poolToken();
USDC = IERC20(
USDC_ADDRESS
);
WETH = IWETH(
WETH_ADDRESS
);
IChainLink chainLinkFeed = IChainLink(
0x986b5E1e1755e3C2440e960477f25201B0a8bbD4
);
tokens[0] = TokenData({
tokenERC20: USDC,
feedLink: chainLinkFeed,
feedDecimals: chainLinkFeed.decimals(),
tokenDecimals: USDC.decimals()
});
_validateData();
tokenProfit = ITokenProfit(
_tokenProfitAddress
);
TOKEN_PROFIT_ADDRESS = _tokenProfitAddress;
UNIV2_ROUTER_ADDRESS = _uniV2RouterAddress;
}
function _validateData()
private
{
for (uint256 i = 0; i < TOKENS; i++) {
TokenData memory token = tokens[i];
if (token.tokenDecimals == 0) {
revert InvalidDecimals();
}
if (token.feedDecimals == 0) {
revert InvalidDecimals();
}
if (token.tokenERC20 == IERC20(ZERO_ADDRESS)) {
revert InvalidToken();
}
if (token.feedLink == IChainLink(ZERO_ADDRESS)) {
revert InvalidFeed();
}
string memory expectedFeedName = string.concat(
token.tokenERC20.symbol(),
" / "
"ETH"
);
string memory chainLinkFeedName = token.feedLink.description();
require(
keccak256(abi.encodePacked(expectedFeedName)) ==
keccak256(abi.encodePacked(chainLinkFeedName)),
"AdapterDeclarations: INVALID_CHAINLINK_FEED"
);
recalibrate(
address(token.feedLink)
);
}
}
function getLatestAggregatorRoundId(
IChainLink _feed
)
public
view
returns (uint80)
{
(
uint80 roundId,
,
,
,
) = _feed.latestRoundData();
return uint64(roundId);
}
function _getIterationCount(
uint80 _latestAggregatorRoundId
)
internal
pure
returns (uint80)
{
return _latestAggregatorRoundId > MAX_ROUND_COUNT
? MAX_ROUND_COUNT
: _latestAggregatorRoundId;
}
function _getRoundTimestamp(
IChainLink _feed,
uint16 _phaseId,
uint80 _aggregatorRoundId
)
internal
view
returns (uint256)
{
(
,
,
,
uint256 timestamp,
) = _feed.getRoundData(
getRoundIdByByteShift(
_phaseId,
_aggregatorRoundId
)
);
return timestamp;
}
function getRoundIdByByteShift(
uint16 _phaseId,
uint80 _aggregatorRoundId
)
public
pure
returns (uint80)
{
return uint80(uint256(_phaseId) << 64 | _aggregatorRoundId);
}
function recalibrate(
address _feed
)
public
{
chainLinkHeartBeat[_feed] = recalibratePreview(
IChainLink(_feed)
);
}
function recalibratePreview(
IChainLink _feed
)
public
view
returns (uint256)
{
uint80 latestAggregatorRoundId = getLatestAggregatorRoundId(
_feed
);
uint80 iterationCount = _getIterationCount(
latestAggregatorRoundId
);
if (iterationCount < 2) {
revert("LiquidRouter: SMALL_SAMPLE");
}
uint16 phaseId = _feed.phaseId();
uint256 latestTimestamp = _getRoundTimestamp(
_feed,
phaseId,
latestAggregatorRoundId
);
uint256 currentDiff;
uint256 currentBiggest;
uint256 currentSecondBiggest;
for (uint80 i = 1; i < iterationCount; i++) {
uint256 currentTimestamp = _getRoundTimestamp(
_feed,
phaseId,
latestAggregatorRoundId - i
);
currentDiff = latestTimestamp - currentTimestamp;
latestTimestamp = currentTimestamp;
if (currentDiff >= currentBiggest) {
currentSecondBiggest = currentBiggest;
currentBiggest = currentDiff;
} else if (currentDiff > currentSecondBiggest && currentDiff < currentBiggest) {
currentSecondBiggest = currentDiff;
}
}
return currentSecondBiggest;
}
function setApprovals()
external
{
address[2] memory spenders = [
UNIV2_ROUTER_ADDRESS,
LIQUID_NFT_ROUTER_ADDRESS
];
for (uint256 i = 0; i < spenders.length; i++) {
for (uint256 j = 0; j < tokens.length; j++) {
tokenProfit.executeAdapterRequest(
address(tokens[j].tokenERC20),
abi.encodeWithSelector(
IERC20.approve.selector,
spenders[i],
UINT256_MAX
)
);
}
tokenProfit.executeAdapterRequest(
WETH_ADDRESS,
abi.encodeWithSelector(
IERC20.approve.selector,
spenders[i],
UINT256_MAX
)
);
}
}
}
文件 3 的 7:AdapterHelper.sol
pragma solidity =0.8.19;
import "./AdapterDeclarations.sol";
error SlippageTooBig();
error ChainLinkOffline();
abstract contract AdapterHelper is AdapterDeclarations {
function _executeSwapWithValue(
address[] memory _path,
uint256 _amount,
uint256 _minAmountOut
)
internal
returns (uint256[] memory)
{
if (_minAmountOut == 0) {
revert SlippageTooBig();
}
bytes memory callbackData = tokenProfit.executeAdapterRequestWithValue(
UNIV2_ROUTER_ADDRESS,
abi.encodeWithSelector(
IUniswapV2.swapExactETHForTokens.selector,
_minAmountOut,
_path,
TOKEN_PROFIT_ADDRESS,
block.timestamp
),
_amount
);
return abi.decode(
callbackData,
(
uint256[]
)
);
}
function isChainlinkOffline()
public
view
returns (bool)
{
for (uint256 i = 0; i < TOKENS; i++) {
IChainLink feed = tokens[i].feedLink;
( ,
,
,
uint256 upd
,
) = feed.latestRoundData();
upd = block.timestamp > upd
? block.timestamp - upd
: block.timestamp;
if (upd > chainLinkHeartBeat[address(feed)]) return true;
}
return false;
}
function getTokenAmounts()
public
view
returns (
uint256 etherAmount,
uint256[] memory tokensAmounts,
uint256 availableEther,
uint256[] memory availableAmounts
)
{
uint256[] memory tokenAmounts = new uint256[](TOKENS);
uint256[] memory availableTokens = new uint256[](TOKENS);
(
availableEther,
availableTokens
) = _getAvailableFunds();
for (uint256 i = 0; i < TOKENS; i++) {
tokenAmounts[i] = _getReservesByToken(
tokens[i].tokenERC20
) + availableTokens[i];
}
etherAmount = _calculateAmountFromShares(
liquidNFTsWETHPool,
TOKEN_PROFIT_ADDRESS
) + availableEther;
return (
etherAmount,
tokenAmounts,
availableEther,
availableTokens
);
}
function _calculateTotalEthValue(
uint256 _msgValue
)
internal
view
returns (uint256)
{
if (isChainlinkOffline() == true) {
revert ChainLinkOffline();
}
(
uint256 etherAmount,
uint256[] memory tokensAmounts,
,
) = getTokenAmounts();
for (uint256 i = 0; i < TOKENS; i++) {
TokenData memory token = tokens[i];
IChainLink feed = token.feedLink;
uint256 latestAnswer = feed.latestAnswer();
require(
latestAnswer > 0,
"AdapterHelper: CHAINLINK_OFFLINE"
);
etherAmount += feed.latestAnswer()
* PRECISION_FACTOR
* tokensAmounts[i]
/ (10 ** token.tokenDecimals)
/ (10 ** token.feedDecimals);
}
return etherAmount
- _msgValue;
}
function _executeSwap(
address[] memory _path,
uint256 _amount,
uint256 _minAmountOut
)
internal
returns (uint256[] memory)
{
if (_minAmountOut == 0) {
revert SlippageTooBig();
}
bytes memory callbackData = tokenProfit.executeAdapterRequest(
UNIV2_ROUTER_ADDRESS,
abi.encodeWithSelector(
IUniswapV2.swapExactTokensForETH.selector,
_amount,
_minAmountOut,
_path,
TOKEN_PROFIT_ADDRESS,
block.timestamp
)
);
return abi.decode(
callbackData,
(
uint256[]
)
);
}
function _unwrapETH(
uint256 _amount
)
internal
{
tokenProfit.executeAdapterRequest(
WETH_ADDRESS,
abi.encodeWithSelector(
IWETH.withdraw.selector,
_amount
)
);
}
function _wrapETH(
uint256 _amount
)
internal
{
tokenProfit.executeAdapterRequestWithValue(
WETH_ADDRESS,
abi.encodeWithSelector(
IWETH.deposit.selector
),
_amount
);
}
function _depositLiquidNFTsWrapper(
ILiquidNFTsPool _pool,
uint256 _amount
)
internal
{
tokenProfit.executeAdapterRequest(
LIQUID_NFT_ROUTER_ADDRESS,
abi.encodeWithSelector(
ILiquidNFTsRouter.depositFunds.selector,
_amount,
_pool
)
);
}
function _withdrawLiquidNFTsWrapper(
ILiquidNFTsPool _pool,
uint256 _amount
)
internal
{
tokenProfit.executeAdapterRequest(
LIQUID_NFT_ROUTER_ADDRESS,
abi.encodeWithSelector(
ILiquidNFTsRouter.withdrawFunds.selector,
_calculateSharesFromAmount(
_pool,
_amount
),
_pool
)
);
}
function _USDCRoutine(
uint256 _amount
)
internal
returns (uint256)
{
uint256 balanceBefore = USDC.balanceOf(
TOKEN_PROFIT_ADDRESS
);
_withdrawLiquidNFTsWrapper(
liquidNFTsUSDCPool,
_amount
);
uint256 balanceAfter = USDC.balanceOf(
TOKEN_PROFIT_ADDRESS
);
return balanceAfter - balanceBefore;
}
function _WETHRoutine(
uint256 _amount
)
internal
returns (uint256)
{
_withdrawLiquidNFTsWrapper(
liquidNFTsWETHPool,
_amount
);
uint256 balance = WETH.balanceOf(
TOKEN_PROFIT_ADDRESS
);
_unwrapETH(
balance
);
return balance;
}
function _getAvailableFunds()
internal
view
returns (
uint256,
uint256[] memory
)
{
uint256[] memory availableTokens = new uint256[](TOKENS);
for (uint256 i = 0; i < TOKENS; i++) {
IERC20 token = tokens[i].tokenERC20;
availableTokens[i] = token.balanceOf(
TOKEN_PROFIT_ADDRESS
);
}
uint256 availableEther = TOKEN_PROFIT_ADDRESS.balance;
return (
availableEther,
availableTokens
);
}
function _getReservesByToken(
IERC20 _token
)
internal
view
returns (uint256)
{
if (_token == USDC) {
return _calculateAmountFromShares(
liquidNFTsUSDCPool,
TOKEN_PROFIT_ADDRESS
);
}
return 0;
}
function _calculateSharesFromAmount(
ILiquidNFTsPool _pool,
uint256 _amount
)
internal
view
returns (uint256)
{
return _amountSharesCalculationWrapper(
_pool.totalInternalShares(),
_pool.pseudoTotalTokensHeld(),
_amount
);
}
function _calculateAmountFromShares(
ILiquidNFTsPool _pool,
address _sharesHolder
)
internal
view
returns (uint256)
{
return _amountSharesCalculationWrapper(
_pool.pseudoTotalTokensHeld(),
_pool.totalInternalShares(),
_pool.internalShares(
_sharesHolder
)
);
}
function _amountSharesCalculationWrapper(
uint256 _totalValue,
uint256 _correspondingTotalValue,
uint256 _amountValue
)
internal
pure
returns (uint256)
{
return _totalValue
* _amountValue
/ _correspondingTotalValue;
}
}
文件 4 的 7:AdapterInterfaces.sol
pragma solidity =0.8.19;
import "./IERC20.sol";
interface IChainLink {
function decimals()
external
view
returns (uint8);
function latestAnswer()
external
view
returns (uint256);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answerdInRound
);
function getRoundData(
uint80 _roundId
)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function phaseId()
external
view
returns(
uint16 phaseId
);
function aggregator()
external
view
returns (address);
function description()
external
view
returns (string memory);
}
interface ITokenProfit {
function getAvailableMint()
external
view
returns (uint256);
function executeAdapterRequest(
address _contractToCall,
bytes memory _callBytes
)
external
returns (bytes memory);
function executeAdapterRequestWithValue(
address _contractToCall,
bytes memory _callBytes,
uint256 _value
)
external
returns (bytes memory);
function totalSupply()
external
view
returns (uint256);
}
interface ILiquidNFTsRouter {
function depositFunds(
uint256 _amount,
address _pool
)
external;
function withdrawFunds(
uint256 _amount,
address _pool
)
external;
}
interface ILiquidNFTsPool {
function pseudoTotalTokensHeld()
external
view
returns (uint256);
function totalInternalShares()
external
view
returns (uint256);
function manualSyncPool()
external;
function internalShares(
address _user
)
external
view
returns (uint256);
function poolToken()
external
view
returns (address);
function chainLinkETH()
external
view
returns (address);
}
interface IUniswapV2 {
function swapExactETHForTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
)
external
payable
returns (uint256[] memory amounts);
function swapExactTokensForETH(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
)
external
returns (uint256[] memory amounts);
}
interface IWETH is IERC20 {
function deposit()
payable
external;
function withdraw(
uint256 _amount
)
external;
}
文件 5 的 7:IERC20.sol
pragma solidity =0.8.19;
interface IERC20 {
function transfer(
address _to,
uint256 _amount
)
external;
function transferFrom(
address _from,
address _to,
uint256 _amount
)
external;
function balanceOf(
address _account
)
external
view
returns (uint256);
function approve(
address _spender,
uint256 _amount
)
external;
function allowance(
address _user,
address _spender
)
external
view
returns (uint256);
function decimals()
external
view
returns (uint8);
function symbol()
external
view
returns (string memory);
}
文件 6 的 7:TokenBase.sol
pragma solidity =0.8.19;
import "./IERC20.sol";
contract TokenBase {
string public constant name = "WISEr Token";
string public constant symbol = "WISEr";
uint8 public constant decimals = 18;
address constant ZERO_ADDRESS = address(0);
uint256 constant UINT256_MAX = type(uint256).max;
uint256 public totalSupply;
uint256 public constant INITIAL_TOTAL_SUPPLY = 1E27;
uint256 public constant MIN_ETH_AMOUNT = 1E5;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
mapping(address => uint256) public nonces;
bytes32 public immutable DOMAIN_SEPARATOR;
bytes32 public constant PERMIT_TYPEHASH = keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
event Received(
address indexed from,
uint256 indexed amount
);
event SupplyPurchase(
address indexed buyer,
uint256 ethAmount,
uint256 tokenAmountReceived,
uint256 indexed appliedFee
);
event RewardsRedeemed(
uint256 _burnAmount,
uint256[] tokenRedeemAmounts,
uint256 etherRedeemAmount
);
constructor() {
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256(bytes("1")),
block.chainid,
address(this)
)
);
}
function _mint(
address _to,
uint256 _value
)
internal
{
totalSupply =
totalSupply + _value;
unchecked {
balanceOf[_to] =
balanceOf[_to] + _value;
}
emit Transfer(
ZERO_ADDRESS,
_to,
_value
);
}
function _burn(
address _from,
uint256 _value
)
internal
{
unchecked {
totalSupply =
totalSupply - _value;
}
balanceOf[_from] =
balanceOf[_from] - _value;
emit Transfer(
_from,
ZERO_ADDRESS,
_value
);
}
function _approve(
address _owner,
address _spender,
uint256 _value
)
private
{
allowance[_owner][_spender] = _value;
emit Approval(
_owner,
_spender,
_value
);
}
function _transfer(
address _from,
address _to,
uint256 _value
)
private
{
balanceOf[_from] =
balanceOf[_from] - _value;
unchecked {
balanceOf[_to] =
balanceOf[_to] + _value;
}
emit Transfer(
_from,
_to,
_value
);
}
function approve(
address _spender,
uint256 _value
)
external
returns (bool)
{
_approve(
msg.sender,
_spender,
_value
);
return true;
}
function increaseAllowance(
address _spender,
uint256 _addedValue
)
external
returns (bool)
{
_approve(
msg.sender,
_spender,
allowance[msg.sender][_spender] + _addedValue
);
return true;
}
function decreaseAllowance(
address _spender,
uint256 _subtractedValue
)
external
returns (bool)
{
_approve(
msg.sender,
_spender,
allowance[msg.sender][_spender] - _subtractedValue
);
return true;
}
function transfer(
address _to,
uint256 _value
)
external
returns (bool)
{
_transfer(
msg.sender,
_to,
_value
);
return true;
}
function transferFrom(
address _from,
address _to,
uint256 _value
)
external
returns (bool)
{
if (allowance[_from][msg.sender] != UINT256_MAX) {
allowance[_from][msg.sender] -= _value;
}
_transfer(
_from,
_to,
_value
);
return true;
}
function permit(
address _owner,
address _spender,
uint256 _value,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
)
external
{
require(
_deadline >= block.timestamp,
"TokenBase: PERMIT_CALL_EXPIRED"
);
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(
abi.encode(
PERMIT_TYPEHASH,
_owner,
_spender,
_value,
nonces[_owner]++,
_deadline
)
)
)
);
if (uint256(_s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
revert("TokenBase: INVALID_SIGNATURE");
}
address recoveredAddress = ecrecover(
digest,
_v,
_r,
_s
);
require(
recoveredAddress != ZERO_ADDRESS &&
recoveredAddress == _owner,
"TokenBase: INVALID_SIGNATURE"
);
_approve(
_owner,
_spender,
_value
);
}
}
文件 7 的 7:TokenProfit.sol
pragma solidity =0.8.19;
import "./Adapter.sol";
import "./TokenBase.sol";
error Prevented();
error ExecuteFailed();
error InvalidAmount(
uint256 provided,
uint256 required
);
contract TokenProfit is TokenBase {
Adapter public adapter;
address public adapterContract;
address public governanceContract;
address public immutable auctionContract;
uint256 constant PRECISION_FACTOR = 1E18;
bool public extraMintAllowed;
uint256 status;
uint256 constant ENTERED = 1;
uint256 constant NOT_ENTERED = 2;
modifier nonReentrant() {
nonReentrantBefore();
_;
nonReentrantAfter();
}
function nonReentrantBefore()
private
{
if (status == ENTERED) {
revert Prevented();
}
status = ENTERED;
}
function nonReentrantAfter()
private
{
status = NOT_ENTERED;
}
modifier onlyAdapter() {
require(
msg.sender == adapterContract,
"TokenProfit: NOT_ADAPTER"
);
_;
}
modifier onlyGovernance() {
require(
msg.sender == governanceContract,
"TokenProfit: NOT_GOVERNANCE"
);
_;
}
modifier syncAdapter() {
adapter.syncServices();
_;
}
receive()
external
payable
{
emit Received(
msg.sender,
msg.value
);
}
constructor(
address _auctionContract,
address _uniV2RouterAddress,
address _liquidNFTsRouterAddress,
address _liquidNFTsWETHPool,
address _liquidNFTsUSDCPool
) {
adapter = new Adapter(
address(this),
_uniV2RouterAddress,
_liquidNFTsRouterAddress,
_liquidNFTsWETHPool,
_liquidNFTsUSDCPool
);
auctionContract = _auctionContract;
adapterContract = address(adapter);
governanceContract = msg.sender;
status = NOT_ENTERED;
}
function executeAdapterRequest(
address _contractAddress,
bytes memory _callBytes
)
external
onlyAdapter
returns (bytes memory)
{
(
bool success,
bytes memory returnData
) = _contractAddress.call(
_callBytes
);
if (success == false) {
revert ExecuteFailed();
}
return returnData;
}
function executeAdapterRequestWithValue(
address _callAddress,
bytes memory _callBytes,
uint256 _callValue
)
external
onlyAdapter
returns (bytes memory)
{
(
bool success,
bytes memory returnData
) = _callAddress.call{value: _callValue}(
_callBytes
);
if (success == false) {
revert ExecuteFailed();
}
return returnData;
}
function changeAdapter(
address _newAdapterAddress
)
external
onlyGovernance
{
adapterContract = _newAdapterAddress;
adapter = Adapter(
_newAdapterAddress
);
}
function getTotalTokenAmount(
uint8 _index
)
external
view
returns (uint256)
{
(
,
uint256[] memory tokenAmounts,
,
) = adapter.getTokenAmounts();
return tokenAmounts[_index];
}
function redeemRewards(
uint256 _burnAmount
)
external
syncAdapter
nonReentrant
returns (
uint256,
uint256[] memory
)
{
(
uint256 availableEther,
uint256[] memory availableTokens,
uint256 etherRedeemAmount,
uint256[] memory tokenRedeemAmounts
) = getUserRedeemAmounts(
_burnAmount
);
_burn(
msg.sender,
_burnAmount
);
_processTokens(
availableTokens,
tokenRedeemAmounts
);
_processEther(
availableEther,
etherRedeemAmount
);
emit RewardsRedeemed(
_burnAmount,
tokenRedeemAmounts,
etherRedeemAmount
);
return (
etherRedeemAmount,
tokenRedeemAmounts
);
}
function getAvailableMint()
public
view
returns (uint256 res)
{
if (totalSupply > INITIAL_TOTAL_SUPPLY) {
return 0;
}
res = INITIAL_TOTAL_SUPPLY
- totalSupply;
}
function getUserRedeemAmounts(
uint256 _burnAmount
)
public
view
returns (
uint256,
uint256[] memory,
uint256,
uint256[] memory
)
{
(
uint256 etherAmount,
uint256[] memory tokenAmounts,
uint256 availableEther,
uint256[] memory availableTokens
) = adapter.getTokenAmounts();
uint256 length = tokenAmounts.length;
uint256[] memory tokenRedeemAmounts = new uint256[](length);
for (uint256 i = 0; i < length; i++) {
tokenRedeemAmounts[i] = _getRedeemAmount(
_burnAmount,
tokenAmounts[i],
totalSupply
);
}
uint256 etherRedeemAmount = _getRedeemAmount(
_burnAmount,
etherAmount,
totalSupply
);
return (
availableEther,
availableTokens,
etherRedeemAmount,
tokenRedeemAmounts
);
}
function _getRedeemAmount(
uint256 _burnAmount,
uint256 _tokenAmount,
uint256 _totalSupply
)
internal
pure
returns (uint256)
{
return _burnAmount
* PRECISION_FACTOR
* _tokenAmount
/ PRECISION_FACTOR
/ _totalSupply;
}
function _processTokens(
uint256[] memory _available,
uint256[] memory _redeemAmounts
)
internal
{
for (uint256 i = 0; i < _available.length; i++) {
if (_redeemAmounts[i] > _available[i]) {
_redeemAmounts[i] = _available[i] + adapter.assistWithdrawTokens(
i,
_redeemAmounts[i] - _available[i]
);
}
(
IERC20 token
,
,
,
) = adapter.tokens(
i
);
token.transfer(
msg.sender,
_redeemAmounts[i]
);
}
}
function _processEther(
uint256 _available,
uint256 _redeemAmount
)
internal
returns (uint256)
{
if (_redeemAmount > _available) {
_redeemAmount = _available + adapter.assistWithdrawETH(
_redeemAmount - _available
);
}
payable(msg.sender).transfer(
_redeemAmount
);
return _redeemAmount;
}
function mintSupply(
address _mintTo,
uint256 _mintAmount
)
external
returns (bool)
{
require(
msg.sender == auctionContract,
"TokenProfit: INVALID_MINTER"
);
_mint(
_mintTo,
_mintAmount
);
return true;
}
function buySupply(
uint256 _desiredAmount
)
external
payable
nonReentrant
returns (uint256)
{
require(
extraMintAllowed == true,
"TokenProfit: MINT_NOT_ALLOWED"
);
require(
getAvailableMint() >= _desiredAmount,
"TokenProfit: MINT_CAPPED"
);
uint256 ethRequired = adapter.getEthAmountFromTokenAmount(
_desiredAmount,
msg.value
);
require(
ethRequired > MIN_ETH_AMOUNT,
"TokenProfit: MINT_TOO_SMALL"
);
if (msg.value < ethRequired) {
revert InvalidAmount(
msg.value,
ethRequired
);
}
_mint(
msg.sender,
_desiredAmount
);
payable(msg.sender).transfer(
msg.value - ethRequired
);
emit SupplyPurchase(
msg.sender,
ethRequired,
_desiredAmount,
adapter.buyFee()
);
return ethRequired;
}
function setAllowMint(
bool _allow
)
external
onlyGovernance
{
extraMintAllowed = _allow;
}
function getCurrentBuyFee()
external
view
returns (uint256)
{
return adapter.buyFee();
}
}
{
"compilationTarget": {
"TokenProfit.sol": "TokenProfit"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_auctionContract","type":"address"},{"internalType":"address","name":"_uniV2RouterAddress","type":"address"},{"internalType":"address","name":"_liquidNFTsRouterAddress","type":"address"},{"internalType":"address","name":"_liquidNFTsWETHPool","type":"address"},{"internalType":"address","name":"_liquidNFTsUSDCPool","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ExecuteFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"provided","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"Prevented","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_burnAmount","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"tokenRedeemAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"etherRedeemAmount","type":"uint256"}],"name":"RewardsRedeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmountReceived","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"appliedFee","type":"uint256"}],"name":"SupplyPurchase","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":"INITIAL_TOTAL_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_ETH_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adapter","outputs":[{"internalType":"contract Adapter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adapterContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"auctionContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_desiredAmount","type":"uint256"}],"name":"buySupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAdapterAddress","type":"address"}],"name":"changeAdapter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contractAddress","type":"address"},{"internalType":"bytes","name":"_callBytes","type":"bytes"}],"name":"executeAdapterRequest","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_callAddress","type":"address"},{"internalType":"bytes","name":"_callBytes","type":"bytes"},{"internalType":"uint256","name":"_callValue","type":"uint256"}],"name":"executeAdapterRequestWithValue","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"extraMintAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAvailableMint","outputs":[{"internalType":"uint256","name":"res","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBuyFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"_index","type":"uint8"}],"name":"getTotalTokenAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_burnAmount","type":"uint256"}],"name":"getUserRedeemAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governanceContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_mintTo","type":"address"},{"internalType":"uint256","name":"_mintAmount","type":"uint256"}],"name":"mintSupply","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":[{"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":"_burnAmount","type":"uint256"}],"name":"redeemRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_allow","type":"bool"}],"name":"setAllowMint","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":"_to","type":"address"},{"internalType":"uint256","name":"_value","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":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]