// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @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.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 2 of 8: EEFI_Furnace.sol
// SPDX-License-Identifier: GPL-3.0-only/* ========== Requirements and Imports ==========
================================================
*/pragmasolidity 0.8.9;import {IWETH} from"IERCWETHInterfaces.sol";
import {IEEFIToken} from"IERCWETHInterfaces.sol";
import {ITokenRewards} from"ITokenRewards.sol";
import {IPodToken} from"IPodToken.sol";
import"Ownable.sol";
import"TransferHelper.sol";
/* ========== Contract ==========
=================================
*/contractEEFIFurnaceisOwnable{
// State Variables ////Addressesaddressprivateconstant _WETH =0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
addressprivateconstant _EEFI =0x857FfC55B1Aa61A7fF847C82072790cAE73cd883;
addressprivateconstant _USDT =0xdAC17F958D2ee523a2206206994597C13D831ec7;
addressprivateconstant _PEAS =0x02f92800F57BCD74066F5709F1Daa1A4302Df875;
//New partner address (receives fees)addresspublic newPartnerAddress;
//Treasury addressaddresspayableimmutable TREASURY =payable(0xf950a86013bAA227009771181a885E369e158da3);
//Interfaces //
IWETH publicconstant weth = IWETH(_WETH);
IEEFIToken publicconstant eefi = IEEFIToken(_EEFI);
ITokenRewards public podTokenRewardsContract;
IPodToken public podTokenContract;
//PTokens/PEAS in contract are retained by defaultboolpublic sendToTreasury =false;
//Events //eventEEFIBurn(address source, uint256 amount);
eventPEASRewardsClaimed(address destination, uint256 amount);
eventPEASRewardsTransferred(address destination, uint256 amount);
eventPTokenUnwrapped(address asset, uint256 amount);
eventPartnerAddressChanged(address newAddress);
eventPodRewardsAndTokenContractsChanged(
ITokenRewards newpodTokenRewardsContract,
IPodToken newpodTokenContract
);
constructor(
ITokenRewards _podTokenRewardsContract, // pToken LP Pool Rewards Contract
IPodToken _podTokenContract // pToken Contract) {
require(
address(_podTokenRewardsContract) !=address(0),
"EEFIFurnance: Invalid pod token rewards contract address."
);
require(
address(_podTokenContract) !=address(0),
"EEFIFurnance: Invalid pod token contract address."
);
podTokenRewardsContract = _podTokenRewardsContract;
podTokenContract = _podTokenContract;
}
/**
* @notice Modifier to restrict certain functions to multisig only (Treasury) calls
* @dev Reverts if the caller is not the Treasury.
*/modifiermultiSigOnly() {
require(msg.sender== TREASURY, "EEFIFurnance: Multisig not caller");
_;
}
/**
* @notice Re-set rewards and pod token addresses
* @dev Only owner can initiate token and rewards address changes
*/functionresetRewardsAndTokenAddresses(
ITokenRewards _newRewardsContract,
IPodToken _newPodTokenContract
) externalonlyOwner{
podTokenRewardsContract = _newRewardsContract;
podTokenContract = _newPodTokenContract;
emit PodRewardsAndTokenContractsChanged(
_newRewardsContract,
_newPodTokenContract
);
}
//Transfer ERC20s from contract ///**
* @notice Transfer deposited assets from contract
* @dev Reverts if caller is not the Treasury (multisig)
*/functiontransferAssetsFromFurnance(
IERC20 token,
address to,
uint256 amount
) publicmultiSigOnly{
uint256 erc20balance = token.balanceOf(address(this));
require(
amount <= erc20balance,
"EEFIFurnance: Balance too low to transfer token"
);
TransferHelper.safeTransfer(address(token), to, amount);
}
//Convert Assets sent to Furnance to WETH Using 0x ///**
* @notice Convert non-WETH asset to WETH using 0x.
*/function_convertAssetToWETH(bytescalldata swapCallData,
address spender,
addresspayable swapTarget,
IERC20 sellToken,
IWETH buyToken,
uint256 sellAmount
) internal{
// Give `spender` an allowance to spend this contract's `sellToken`.// Note that for some tokens (e.g., USDT, KNC), any existing// allowance must be set to 0, before being able to update it.require(buyToken == weth, "EEFIFurnance: Buy token not WETH.");
if (address(sellToken) == _USDT) {
//Reset approval to 0uint256 initialUSDTApprovalAmount =0;
TransferHelper.safeApprove(
address(sellToken),
address(spender),
initialUSDTApprovalAmount
);
}
//Approve WETH for intermediate step swaps
TransferHelper.safeApprove(
address(weth),
address(spender),
type(uint256).max
);
//Approve sell token for sell amount
TransferHelper.safeApprove(
address(sellToken),
address(spender),
sellAmount
);
// Call the encoded swap function at `swapTarget`,
(bool success, ) = swapTarget.call{value: msg.value}(swapCallData);
require(success, "EEFIFurnance: Swap failed. Low balance / error.");
}
/**
* @notice Convert non-WETH asset sent to Furnace to WETH.
* @dev Only owner can initiate swap. Owner does not have access to swapped assets.
*/functionconvertAssetToWETH(bytescalldata swapCallData,
address spender,
addresspayable swapTarget,
IERC20 sellToken,
IWETH buyToken,
uint256 sellAmount
) externalpayableonlyOwner{
_convertAssetToWETH(
swapCallData,
spender,
swapTarget,
sellToken,
buyToken,
sellAmount
);
}
//Sell contract WETH for EEFI and Burn ///**
* @notice Purchase EEFI using contract WETH and burn asset.
* @dev Only owner can initiate swap and burn. Requires that contract has WETH and permission to burn EEFI.
*/functionbuyAndBurnEEFI(bytescalldata swapCallData,
address spender,
addresspayable swapTarget,
IWETH sellToken,
IEEFIToken buyToken
) externalpayableonlyOwner{
//Trade WETH for EEFI //require(sellToken == weth, "EEFIFurnance: Sell token not WETH.");
require(buyToken == eefi, "EEFIFurnance: Buy token not EEFI.");
require(
weth.balanceOf(address(this)) >0,
"EEFIFurnance: No WETH available to trade"
);
//Approve WETH for trade
TransferHelper.safeApprove(
address(weth),
address(spender),
type(uint256).max
);
// Call the encoded swap function at `swapTarget`,
(bool success, ) = swapTarget.call{value: msg.value}(swapCallData);
require(success, "EEFIFurnance: Swap failed. Low balance / error.");
//Burn Swapped EEFI //uint256 _contractEEFIAmount = eefi.balanceOf(address(this));
require(
_contractEEFIAmount >0,
"EEFIFurnance: Insufficient EEFI in contract to burn"
);
eefi.burn(_contractEEFIAmount);
emit EEFIBurn(address(this), _contractEEFIAmount);
}
//Claim Peapods LP Rewards and Convert to WETH ///**
* @notice Claim PEAS LP token rewards and Convert to WETH
* @dev Only owner can initiate claim. Owner does not have access to claimed PEAS
*/functionclaimPEASAndConvertToWETH(address claimAddress,
uint256 claimableReward,
bytescalldata swapCallData,
address spender,
addresspayable swapTarget,
IERC20 sellToken,
IWETH buyToken,
uint256 sellAmount,
uint256 buyAmount,
bool sendWETHToTreasury
) externalpayableonlyOwner{
//Claim PEAS rewardsrequire(claimableReward >0, "EEFIFurnance: No rewards to claim");
require(buyToken == weth, "EEFIFurnance: Buy token not WETH");
podTokenRewardsContract.claimReward(claimAddress);
emit PEASRewardsClaimed(claimAddress, claimableReward);
uint256 peasTokenBalance = IERC20(_PEAS).balanceOf(address(this));
require(
peasTokenBalance >0,
"EEFIFurnance: No rewards to convert"//
);
//Convert PEAS to WETH //
_convertAssetToWETH(
swapCallData,
spender,
swapTarget,
sellToken,
buyToken,
sellAmount
);
if (sendWETHToTreasury ==true) {
TransferHelper.safeTransfer(_WETH, TREASURY, buyAmount);
}
}
// Claim Peapods Rewards and Send to Treasury ///**
* @notice Claim PEAS LP token rewards (if rewards are deposited to contract, send to Treasury)
* @dev Only owner can initiate claim.
*/functionclaimLPRewardsAndSendToTreasury(address claimAddress,
uint256 claimableReward
) externalpayableonlyOwner{
//Claim PEAS rewards //require(claimableReward >0, "EEFIFurnance: No rewards to claim");
podTokenRewardsContract.claimReward(claimAddress);
emit PEASRewardsClaimed(claimAddress, claimableReward);
//If any PEAS rewards are in contract, transfer to Treasuryuint256 peasTokenBalance = IERC20(_PEAS).balanceOf(address(this));
if (peasTokenBalance >0) {
TransferHelper.safeTransfer(_PEAS, TREASURY, peasTokenBalance);
emit PEASRewardsTransferred(TREASURY, peasTokenBalance);
}
}
// Unwrap pToken and optionally burn EEFI ///**
* @notice Unwrap pToken; If token is EEFI burn it; Optionally send pAsset to Treasury
* @dev Only owner can initiate unwrap.
*/functionunWrapAndBurnPToken(address pTokenToUnwrap,
uint8 tokenIndex,
bool sendPTokenToTreasury
) externalonlyOwner{
//Unwrap PToken //uint256 pTokenAmount = podTokenContract.balanceOf(address(this));
address[] memory unwrappedPToken =newaddress[](1);
uint8[] memory unwrapPercentage =newuint8[](1);
unwrappedPToken[0] = pTokenToUnwrap;
unwrapPercentage[0] =100;
require(
pTokenAmount >0,
"EEFIFurnance: Not enough pToken in contract to unwrap"
);
podTokenContract.debond(
pTokenAmount,
unwrappedPToken,
unwrapPercentage
);
if (sendPTokenToTreasury ==true) {
//Retrive token address associated with pod asset index
(address podTokenAddress, , , , ) = podTokenContract.indexTokens(
tokenIndex
);
uint256 unwrappedTokenAmount = IERC20(podTokenAddress).balanceOf(
address(this)
);
TransferHelper.safeTransfer(
podTokenAddress,
TREASURY,
unwrappedTokenAmount
);
}
//Burn unwrapped EEFI (or EEFI in contract) //uint256 _contractEEFIAmount = eefi.balanceOf(address(this));
if (_contractEEFIAmount >0) {
eefi.burn(_contractEEFIAmount);
emit EEFIBurn(address(this), _contractEEFIAmount);
}
}
/**
* @notice Re-set partner address (receives fees)
* @dev Only owner can initiate partner address change
*/functionresetPartnerAddress(address _newPartner) externalonlyOwner{
newPartnerAddress = _newPartner;
podTokenContract.setPartner(newPartnerAddress);
emit PartnerAddressChanged(_newPartner);
}
}
Contract Source Code
File 3 of 8: IERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address to, uint256 amount) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom,
address to,
uint256 amount
) externalreturns (bool);
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)pragmasolidity ^0.8.0;import"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.
*
* By default, the owner account will be the one that deploys the contract. 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.
*/abstractcontractOwnableisContext{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Contract Source Code
File 8 of 8: TransferHelper.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=0.6.0;import"IERC20.sol";
libraryTransferHelper{
/// @notice Transfers tokens from the targeted address to the given destination/// @notice Errors with 'STF' if transfer fails/// @param token The contract address of the token to be transferred/// @param from The originating address from which the tokens will be transferred/// @param to The destination address of the transfer/// @param value The amount to be transferredfunctionsafeTransferFrom(address token,
addressfrom,
address to,
uint256 value
) internal{
(bool success, bytesmemory data) =
token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
require(success && (data.length==0||abi.decode(data, (bool))), 'STF');
}
/// @notice Transfers tokens from msg.sender to a recipient/// @dev Errors with ST if transfer fails/// @param token The contract address of the token which will be transferred/// @param to The recipient of the transfer/// @param value The value of the transferfunctionsafeTransfer(address token,
address to,
uint256 value
) internal{
(bool success, bytesmemory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
require(success && (data.length==0||abi.decode(data, (bool))), 'ST');
}
/// @notice Approves the stipulated contract to spend the given allowance in the given token/// @dev Errors with 'SA' if transfer fails/// @param token The contract address of the token to be approved/// @param to The target of the approval/// @param value The amount of the given token the target will be allowed to spendfunctionsafeApprove(address token,
address to,
uint256 value
) internal{
(bool success, bytesmemory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
require(success && (data.length==0||abi.decode(data, (bool))), 'SA');
}
/// @notice Transfers ETH to the recipient address/// @dev Fails with `STE`/// @param to The destination of the transfer/// @param value The value to be transferredfunctionsafeTransferETH(address to, uint256 value) internal{
(bool success, ) = to.call{value: value}(newbytes(0));
require(success, 'STE');
}
}