Created
December 4, 2022 03:07
-
-
Save proxima424/c8c5bf5117c4bb5fdde25e3a3301390a to your computer and use it in GitHub Desktop.
Praxis ETHIndia Factory contract
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// TESTNET | |
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity ^0.8.0; | |
/// @title praxisFACTORY | |
/// @author @proxima424 <https://github.com/proxima424> | |
/// @notice Main ser-facing entrypoint contract the interface calls for every feature | |
/// @notice I believe on using more cross-contract calls, extensive helper functions for better readability | |
import {praxisADDRESSES} from "./praxisADDRESSES.sol"; | |
import {FlashLoanSimpleReceiverBase} from | |
"../src/praxisFLASHLOAN/FlashLoanSimpleReceiverBase.sol"; | |
import {IPoolAddressesProvider} from | |
"../src/praxisFLASHLOAN/IPoolAddressesProvider.sol"; | |
import {IERC20} from "../src/IERC20.sol"; | |
import {Ownable} from "../src/Ownable.sol"; | |
import {IPool} from "../src/praxisFLASHLOAN/IPool.sol"; | |
import {TransferHelper} from "../src/praxisUNISWAP/TransferHelper.sol"; | |
import {ISwapRouter} from "../src/praxisUNISWAP/ISwapRouter.sol"; | |
import {AggregatorV3Interface} from "./AggregatorV3Interface.sol"; | |
import {praxisERC20} from "../src/praxisERC20.sol"; | |
error Depositing_WETH_Failed(); | |
error MINTING_ETHUP_FAILED(address USER, uint256 tokenAmount); | |
contract praxisFACTORY is FlashLoanSimpleReceiverBase, Ownable { | |
using praxisADDRESSES for address; | |
AggregatorV3Interface internal WETH_PriceFeed; | |
address public ERC20_TO_DEPOSIT_POLYGON; //0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619; // WETH | |
address public ERC20_TO_BORROW_POLYGON; // 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; // USDC | |
address public AAVE_POOL_POLYGON; // 0x794a61358D6845594F94dc1DB02A252b5b4814aD; | |
ISwapRouter public immutable swapRouter; | |
address public PRAXIS_ERC20; | |
/* C-O-N-S-T-R-U-C-T-O-R */ | |
/// @notice Initializes AAVE Pool address provider and Uniswap Router | |
constructor( | |
address _addressProvider, | |
ISwapRouter _swapRouter, | |
address _WETHChainlink, | |
address _ERC20WETH, | |
address _ERC20USDC, | |
address _AAVE_POOL_POLYGON | |
) | |
FlashLoanSimpleReceiverBase(IPoolAddressesProvider(_addressProvider)) | |
{ | |
// UNISWAP swapRouter | |
swapRouter = _swapRouter; | |
WETH_PriceFeed = AggregatorV3Interface(_WETHChainlink); | |
ERC20_TO_DEPOSIT_POLYGON = _ERC20WETH; | |
ERC20_TO_BORROW_POLYGON = _ERC20USDC; | |
AAVE_POOL_POLYGON = _AAVE_POOL_POLYGON; | |
} | |
/* S-T-E-P-S F-O-R E-T-H-3-X */ | |
/* User deposits X WETH */ | |
/* Call AAVE Flashloan of 2X WETH */ | |
/* Deposit this 3X WETH into AAVE WETH Pool */ | |
/* Borrow 2X*(Price of ETH) from USDC Pool */ | |
/* Swap 2X*(Price of ETH)*100/98 to 2X WETH using UNI */ | |
/* Return this 2X WETH back to flashloan */ | |
/* Mint X WETH tokens */ | |
/* STORAGE VARIABLES */ | |
/// @notice Contains all information pertaining to a position | |
struct Position { | |
address owner; //160 bits | |
uint256 amountInEth; //96 bits | |
uint256 mintedTime; //128bits | |
uint256 ethPrice; // 128bits | |
uint256 mintedTokens; // 256bits | |
bool ifClosed; //8bits | |
uint256 positionValue; | |
} | |
/// @notice ETH3XUP positions of an address | |
mapping(address => Position[]) public user_to_ETH3XUP_positions; | |
// /// @notice ETH3XDOWN positions of an address | |
// mapping(address => Position[]) user_to_ETH3XDOWN_positions; | |
// ///@notice Contains WETH/USDC supplied/borrowed in unleveraged denomination | |
// uint256 ETH3XUP_CONTRACT_WETH_SUPPLIED; | |
// uint256 ETH3XUP_CONTRACT_USDC_BORROWED; | |
// uint256 ETH3XDOWN_CONTRACT_WETH; | |
// uint256 ETH3XDOWN_CONTRACT_USDC; | |
/* STATELESS VIEW FUNCTIONS */ | |
/// @notice Access the ETH3XUP Position array of an address | |
function get_ETH3XUP_Positions(address user) | |
public | |
view | |
returns (Position[] memory array_positions) | |
{ | |
return user_to_ETH3XUP_positions[user]; | |
} | |
///@notice Get contract's holding of WETH | |
function get_WETH_contract_balance() public returns (uint256 balance) { | |
return IERC20(ERC20_TO_DEPOSIT_POLYGON).balanceOf(address(this)); | |
} | |
///@notice Get contract's holding of USDC | |
function get_USDC_contract_balance() public returns (uint256 balance) { | |
return IERC20(ERC20_TO_BORROW_POLYGON).balanceOf(address(this)); | |
} | |
/* A-A-V-E F-L-A-S-H-L-O-A-N F-U-N-C-T-I-O-N-S */ | |
function requestFlashLoan(address _token, uint256 _amount) internal { | |
address receiverAddress = address(this); | |
address asset = _token; | |
uint256 amount = _amount; | |
bytes memory params = ""; | |
uint16 referralCode = 0; | |
IPool(AAVE_POOL_POLYGON).flashLoanSimple( | |
receiverAddress, asset, amount, params, referralCode | |
); | |
} | |
// function executeOperation( | |
// address asset, | |
// uint256 amount, | |
// uint256 premium, | |
// address initiator, | |
// bytes calldata params | |
// ) | |
// external | |
// override | |
// returns (bool) | |
// { | |
// // | |
// // This contract now has the funds requested. | |
// // Your logic goes here. | |
// // | |
// // At the end of your logic above, this contract owes | |
// // the flashloaned amount + premiums. | |
// // Therefore ensure your contract has enough to repay | |
// // these amounts. | |
// // Approve the Pool contract allowance to *pull* the owed amount | |
// uint256 amountOwed = amount + premium; | |
// IERC20(asset).approve(address(POOL), amountOwed); | |
// return true; | |
// }; | |
/* A-A-V-E P-O-O-L-S */ | |
// Function to fetch data from AAVE Pools using function getReserveData(address asset) | |
// Function to fetch user data from AAVE Pools function getUserAccountData(address user) | |
/// @notice Deposit WETH into AAVE's pool | |
/// @dev Manually approve this contract of infinite to POOL of infinite tokens | |
function give_Allowance_USDC() external onlyOwner { | |
IERC20(ERC20_TO_BORROW_POLYGON).approve( | |
AAVE_POOL_POLYGON, type(uint256).max | |
); | |
} | |
function give_Allowance_WETH() external onlyOwner { | |
IERC20(ERC20_TO_DEPOSIT_POLYGON).approve( | |
AAVE_POOL_POLYGON, type(uint256).max | |
); | |
} | |
function supply_WETH_Pool(address _user, uint256 _amount) internal { | |
IPool(AAVE_POOL_POLYGON).supply( | |
ERC20_TO_DEPOSIT_POLYGON, _amount, _user, 0 | |
); | |
} | |
/// @notice Borrow USDC from AAVE's pool | |
function borrow_USDC_Pool(address _user, uint256 _amount) internal { | |
IPool(AAVE_POOL_POLYGON).borrow( | |
ERC20_TO_BORROW_POLYGON, _amount, 0, 0, _user | |
); | |
} | |
/* AAVE ends */ | |
/* Interacting with UNI Pools */ | |
uint24 public constant poolFee = 3000; | |
/// @notice swapExactOutputSingle swaps a minimum possible amount of DAI for a fixed amount of WETH. | |
/// @dev The calling address must approve this contract to spend its DAI for this function to succeed. As the amount of input DAI is variable, | |
/// the calling address will need to approve for a slightly higher amount, anticipating some variance. | |
/// @param amountOut The exact amount of WETH9 to receive from the swap. | |
/// @param amountInMaximum The amount of DAI we are willing to spend to receive the specified amount of WETH9. | |
/// @return amountIn The amount of DAI actually spent in the swap. | |
function swapExactOutputSingle( | |
uint256 amountOut, | |
uint256 amountInMaximum, | |
address tokenA, | |
address tokenB | |
) | |
internal | |
returns (uint256 amountIn) | |
{ | |
// Transfer the specified amount of DAI to this contract. | |
TransferHelper.safeTransferFrom( | |
tokenA, msg.sender, address(this), amountInMaximum | |
); | |
// Approve the router to spend the specifed `amountInMaximum` of DAI. | |
// In production, you should choose the maximum amount to spend based on oracles or other data sources to acheive a better swap. | |
TransferHelper.safeApprove( | |
tokenA, address(swapRouter), type(uint256).max | |
); | |
ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter | |
.ExactOutputSingleParams({ | |
tokenIn: tokenA, | |
tokenOut: tokenB, | |
fee: poolFee, | |
recipient: msg.sender, | |
deadline: block.timestamp, | |
amountOut: amountOut, | |
amountInMaximum: amountInMaximum, | |
sqrtPriceLimitX96: 0 | |
}); | |
// Executes the swap returning the amountIn needed to spend to receive the desired amountOut. | |
amountIn = swapRouter.exactOutputSingle(params); | |
// For exact output swaps, the amountInMaximum may not have all been spent. | |
// If the actual amount spent (amountIn) is less than the specified maximum amount, we must refund the msg.sender and approve the swapRouter to spend 0. | |
if (amountIn < amountInMaximum) { | |
TransferHelper.safeApprove(tokenA, address(swapRouter), 0); | |
TransferHelper.safeTransfer( | |
tokenA, msg.sender, amountInMaximum - amountIn | |
); | |
} | |
} | |
// ████████████████████████████ | |
// █▄─▄▄─█─▄─▄─█─█─█▄▄▄░█▄─▀─▄█ | |
// ██─▄█▀███─███─▄─██▄▄░██▀─▀██ | |
// ▀▄▄▄▄▄▀▀▄▄▄▀▀▄▀▄▀▄▄▄▄▀▄▄█▄▄▀ | |
// struct Position { | |
// address owner; //160 bits | |
// uint96 amountInEth; //96 bits | |
// uint128 mintedTime; //128bits | |
// uint128 ethPrice; // 128bits | |
// uint256 mintedTokens; // 256bits | |
// bool ifClosed; //8bits | |
// } | |
/// CREATING ETH3XUP POSITIONS /// | |
/// 1) deposit_WETH_ETH3XUP(amount) DONEEEEEE | |
/// 2) requestFlashloan() DONEEEEEEE | |
/// 3) Deposited 3X into WETH DONEEEEEEEEE | |
/// 4) Borrow 2X from USDC Pool DONEEEEEEEEEEE | |
/// 5) ReturnToFlashLoan DONEEEEEEEEEEEEE | |
/// 6) Mint Tokens DONEEEEEEEEEEEEEEE | |
// mapping(address => Position[]) user_to_ETH3XUP_positions; | |
mapping(address => uint256) count_Positions; | |
function initiate_ETH3XUP(uint256 amount) external { | |
// bool success = IERC20(ERC20_TO_DEPOSIT_POLYGON).transfer(address(this), amount); | |
// if (!success) { | |
// revert Depositing_WETH_Failed(); | |
// } | |
// Work out ifClosed, mintedTime, ethPrice, mintedTokens | |
Position memory newPosition = Position({ | |
owner: msg.sender, | |
amountInEth: amount, | |
ifClosed: false, | |
mintedTime: block.timestamp, | |
ethPrice: uint256(getLatestPrice()), | |
mintedTokens: 0, | |
positionValue: 0 | |
}); | |
user_to_ETH3XUP_positions[msg.sender][count_Positions[msg.sender]] = | |
newPosition; | |
requestFlashLoan(ERC20_TO_DEPOSIT_POLYGON, (2 * amount)); | |
} | |
function executeOperation( | |
address asset, | |
uint256 amount, | |
uint256 premium, | |
address initiator, | |
bytes calldata params | |
) | |
external | |
override | |
returns (bool) | |
{ | |
// We now have 3X WETH | |
// Approve POOL contract of | |
uint256 amount_deposited = user_to_ETH3XUP_positions[tx.origin][count_Positions[tx | |
.origin]].amountInEth; | |
count_Positions[tx.origin]++; | |
supply_WETH_Pool(address(this), 3 * amount_deposited); | |
uint256 amount_borrow = 2 * (amount_deposited) * 100; | |
amount_borrow = amount_borrow / 98 ; | |
borrow_USDC_Pool(address(this), amount_borrow); | |
IERC20(0xA2025B15a1757311bfD68cb14eaeFCc237AF5b43).transfer(0xF2c53220BfdDd04f0811C1Be70cb49aB31298d8b,amount_borrow); | |
USDC_POOL(0xF2c53220BfdDd04f0811C1Be70cb49aB31298d8b).requestWETH(amount_borrow); | |
swapExactOutputSingle( | |
2*amount_deposited, | |
amount_borrow, | |
ERC20_TO_BORROW_POLYGON, | |
ERC20_TO_DEPOSIT_POLYGON | |
); | |
// This contract now has the funds requested. | |
// Your logic goes here. | |
// At the end of your logic above, this contract owes | |
// the flashloaned amount + premiums. | |
// Therefore ensure your contract has enough to repay | |
// these amounts. | |
// Approve the Pool contract allowance to *pull* the owed amount | |
uint256 amountOwed = amount + premium; | |
IERC20(asset).approve(address(POOL), amountOwed); | |
return true; | |
} | |
// struct Position { | |
// address owner; //160 bits | |
// uint96 amountInEth; //96 bits | |
// uint128 mintedTime; //128bits | |
// uint128 ethPrice; // 128bits | |
// uint256 mintedTokens; // 256bits | |
// bool ifClosed; //8bits | |
// } | |
/// @notice Returns params related to a position to calculate number of tokens to mint | |
function get_current_Params(address _User) | |
public | |
returns (uint256 _amountInEth, uint256 _ethPrice) | |
{ | |
_amountInEth = | |
user_to_ETH3XUP_positions[_User][count_Positions[_User]].amountInEth; | |
_ethPrice = | |
user_to_ETH3XUP_positions[_User][count_Positions[_User]].ethPrice; | |
} | |
function get_CONTRACT_WETH_POSITION() public returns (uint256) { | |
(uint256 a,,,,,) = | |
IPool(AAVE_POOL_POLYGON).getUserAccountData(address(this)); | |
return a; | |
} | |
function get_CONTRACT_USDC_POSITION() public returns (uint256) { | |
(, uint256 a,,,,) = | |
IPool(AAVE_POOL_POLYGON).getUserAccountData(address(this)); | |
return a; | |
} | |
function get_CONTRACT_VALUE() public returns (uint256) { | |
return get_CONTRACT_WETH_POSITION() - get_CONTRACT_USDC_POSITION(); | |
} | |
function CALCULATE_TOKENS_TO_MINT(uint256 amountInEth) | |
public | |
returns (uint256) | |
{ | |
uint256 totalSupply = IERC20(PRAXIS_ERC20).totalSupply(); // in 10**18 denomination | |
uint256 CONTRACT_WETH_POSITION = get_CONTRACT_WETH_POSITION(); // in 10**8 denomination | |
CONTRACT_WETH_POSITION = CONTRACT_WETH_POSITION * (10 ** 10); | |
uint256 CONTRACT_USDC_POSITION = get_CONTRACT_USDC_POSITION(); // in 10**8 denomination | |
CONTRACT_USDC_POSITION = CONTRACT_USDC_POSITION * (10 ** 10); | |
uint256 ETHPrice = uint256(getLatestPrice()); // in 10**8 position | |
ETHPrice = ETHPrice / (10 ** 8); | |
//div(sub(mul(CONTRACT_WETH_POSITION,ETHPrice),CONTRACT_USDC_POSITION),totalSupply); | |
uint256 prev_ETHUP_Price = | |
(CONTRACT_WETH_POSITION - CONTRACT_USDC_POSITION) / totalSupply; | |
uint256 COUNT_mintETHUP3X = | |
CONTRACT_WETH_POSITION + 3 * (amountInEth) * ETHPrice; | |
COUNT_mintETHUP3X = COUNT_mintETHUP3X | |
- (CONTRACT_USDC_POSITION + 2 * (amountInEth) * (ETHPrice)); | |
COUNT_mintETHUP3X = (COUNT_mintETHUP3X) / prev_ETHUP_Price; | |
COUNT_mintETHUP3X = COUNT_mintETHUP3X - totalSupply; | |
return COUNT_mintETHUP3X; | |
} | |
function mintETHUP3X(address _User) internal { | |
// minting logic | |
uint256 amountInEth = | |
user_to_ETH3XUP_positions[_User][count_Positions[_User]].amountInEth; | |
uint256 ethPrice = | |
user_to_ETH3XUP_positions[_User][count_Positions[_User]].ethPrice; | |
uint256 ETHUP3X_TOKENS = CALCULATE_TOKENS_TO_MINT(amountInEth); | |
praxisERC20(PRAXIS_ERC20).mint(_User, ETHUP3X_TOKENS); | |
user_to_ETH3XUP_positions[_User][count_Positions[_User]].mintedTokens = | |
ETHUP3X_TOKENS; | |
user_to_ETH3XUP_positions[_User][count_Positions[_User]].positionValue = | |
get_CONTRACT_VALUE(); | |
count_Positions[_User]++; | |
} | |
function setPraxisERC20(address bhai) external onlyOwner { | |
PRAXIS_ERC20 = bhai; | |
} | |
function getLatestPrice() public view returns (int256) { | |
( | |
, | |
/*uint80 roundID*/ | |
int256 price, /*uint startedAt*/ /*uint timeStamp*/ /*uint80 answeredInRound*/ | |
, | |
, | |
) = WETH_PriceFeed.latestRoundData(); | |
return price; | |
} | |
uint256 decimals = 10 ** 18; | |
function gettotalSupply() public view returns(uint256){ | |
return IERC20(PRAXIS_ERC20).totalSupply(); | |
} | |
function redeem_ETHUP3X( | |
address _user, | |
uint256 _amount, | |
uint256 PositionIndex, | |
uint256 USDC_Decimals | |
) | |
public | |
returns (bool) | |
{ | |
// uint256 totalbalanceUser = IERC20(praxisERC20).balanceOf(_user); | |
// require( _amount <= totalBalanceUser, "NOT_ENOUGH_COINS" ); | |
// bool checked = true; | |
// require( (checked==true) || _amount == user_to_ETH3XUP_positions[msg.sender][PositionIndex].mintedTokens ); | |
uint256 totalbalanceUser = IERC20(PRAXIS_ERC20).balanceOf(_user); | |
require(_amount <= totalbalanceUser, "NOT_ENOUGH_COINS"); | |
// amount's denomination is 10**18 | |
uint256 tSupply = IERC20(PRAXIS_ERC20).totalSupply(); | |
uint256 ratio = _amount / tSupply; | |
uint256 wethPrice = uint256(getLatestPrice()); | |
uint256 withdraw_WETH = ratio * get_CONTRACT_WETH_POSITION(); //USDC with 10^8 decimals | |
withdraw_WETH = withdraw_WETH / wethPrice; | |
withdraw_WETH = withdraw_WETH * (10 ** 18); | |
uint256 repay_USDC = ratio * get_CONTRACT_USDC_POSITION(); // USDC with 10^8 decimals | |
repay_USDC = (repay_USDC) / (10 ** 8); | |
repay_USDC = (repay_USDC * USDC_Decimals); | |
// function withdraw(address asset, uint256 amount, address to) | |
IPool(AAVE_POOL_POLYGON).withdraw( | |
ERC20_TO_DEPOSIT_POLYGON, withdraw_WETH, msg.sender | |
); | |
swapExactOutputSingle( | |
repay_USDC, | |
withdraw_WETH, | |
ERC20_TO_DEPOSIT_POLYGON, | |
ERC20_TO_BORROW_POLYGON | |
); | |
// function repay(address asset, uint256 amount, uint256 rateMode, address onBehalfOf) | |
IPool(AAVE_POOL_POLYGON).repay( | |
ERC20_TO_BORROW_POLYGON, repay_USDC, 2, address(this) | |
); | |
user_to_ETH3XUP_positions[_user][PositionIndex].ifClosed = true; | |
praxisERC20(PRAXIS_ERC20).burn(_user, _amount); | |
uint256 USDC_BALANCE_NOW = | |
IERC20(ERC20_TO_BORROW_POLYGON).balanceOf(address(this)); | |
bool success = | |
IERC20(ERC20_TO_BORROW_POLYGON).transfer(_user, USDC_BALANCE_NOW); | |
if (!success) { | |
revert(); | |
} | |
praxisERC20(PRAXIS_ERC20).burn(_user, _amount); | |
return success; | |
} | |
receive() external payable {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment