├── .env ├── .gitignore ├── LICENSE ├── README.md ├── brownie-config.yaml ├── chainlink_mapping.py ├── img ├── aave.png └── python.png ├── interfaces ├── Aave │ ├── ILendingPool.sol │ └── ILendingPoolAddressesProvider.sol ├── Chainlink │ ├── AggregatorV3Interface.sol │ └── LinkTokenInterface.sol ├── IERC20.sol ├── IWeth.sol └── Swap │ ├── IUniswapV2Pair.sol │ └── IUniswapV2Router02.sol ├── requirements.txt ├── scripts ├── __init__.py ├── aave │ ├── __init__.py │ └── aave_borrow.py ├── chainlink │ ├── __init__.py │ └── chainlink.py ├── get_weth.py ├── helpful_scripts.py └── swap │ ├── __init__.py │ ├── short_sell.py │ └── swap.py └── tests ├── test_aave_borrow.py └── test_short_sell.py /.env: -------------------------------------------------------------------------------- 1 | # export WEB3_INFURA_PROJECT_ID=YourProjectID 2 | # export PRIVATE_KEY="0xasdfasdfasdfasd..." 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | build/* 3 | .pytest_cache 4 | .env 5 | build 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Patrick Collins 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # defi_py_mix 4 | 5 | For all your DeFi needs 6 | 7 | [You can see a smaller version of this here. ](https://github.com/PatrickAlphaC/aave_brownie_py) 8 | 9 | In our `aave_borrow.py` script, we do the following: 10 | 11 | 1. Approve our `ETH` to be swapped for `WETH` 12 | 2. Swap an `amount` of `ETH` for `WETH` 13 | 3. Using `deposit_to_aave` we deposit the `WETH` as collateral 14 | 4. We use that collateral to borrow `DAI` with `borrow_erc20` 15 | 5. Then, we pay it back! 16 | 6. We can view the txs on etherscan to see what's going on under the hood. 17 | 18 | In our `short_sell.py` script, we do the following: 19 | 20 | 1. Approve our `ETH` to be swapped for `WETH` 21 | 2. Swap an `amount` of `ETH` for `WETH` 22 | 3. Using `deposit_to_aave` we deposit the `WETH` as collateral 23 | 4. We use that collateral to borrow `DAI` with `borrow_erc20` 24 | 5. We then sell that `DAI` on the `Uniswap` DEX 25 | This is essentailly a short sell on DAI! 26 | 27 | # Setup 28 | 29 | You'll need python, pipx, and eth-brownie installed. 30 | 31 | Ideally you'd install with pipx 32 | ```bash 33 | pip install --user pipx 34 | pipx ensurepath 35 | # restart your terminal 36 | pipx install eth-brownie 37 | ``` 38 | Or, you could try it with pip. 39 | ``` 40 | pip install -r requirements.txt 41 | ``` 42 | Or, if you run into issues use pipx: 43 | 44 | 45 | You'll need the following [environment variables](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html). If you're unfamiliar with environment variables, you can set them all in your `.env` file: 46 | 47 | ``` 48 | export WEB3_INFURA_PROJECT_ID=YourProjectID 49 | export PRIVATE_KEY="0xasdfasdfasdfasd..." 50 | ``` 51 | 52 | - `PRIVATE_KEY`: Your Private Key from your Wallet. *Note: If using metamask, you'll have to add a 0x to the start of your private key. 53 | - `WEB3_INFURA_PROJECT_ID`: Your connection to the blockchain. You can get a URL from a service like [Infura](https://infura.io/)]. Right now it is hard coded to work with infura, but you can modify it however you want using `brownie networks modify`. 54 | 55 | > Note: DO NOT SEND THESE TO GITHUB!!! 56 | 57 | And last, be sure to check the aave_dai_token if you're using a [testnet DAI token](https://docs.aave.com/developers/deployed-contracts/deployed-contracts0). Aave sometimes changes the token they use on testnet to keep liquidity, [please check here for reference](https://aave.github.io/aave-addresses/kovan.json). 58 | Also, feel free to check the [Aave docs](https://docs.aave.com/developers/the-core-protocol/lendingpool) as well, to learn more about the tools we are using. 59 | 60 | # Quickstart - kovan 61 | 62 | 1. [Get some kovan ETH](https://faucet.kovan.network/) 63 | 64 | 2. Get some WETH 65 | 66 | ``` 67 | brownie run scripts/get_weth.py --network kovan 68 | ``` 69 | 70 | 3. Run the script! 71 | 72 | ``` 73 | brownie run scripts/aave_borrow.py --network kovan 74 | ``` 75 | 76 | 77 | # Quickstart - mainnet-fork 78 | 79 | 80 | Optional for running locally: 81 | If you want to run locally, you can install `ganache-cli` and `yarn`. Here is where you can [install yarn.](https://classic.yarnpkg.com/en/docs/install/#mac-stable) 82 | 83 | ``` 84 | yarn global add ganache-cli 85 | ``` 86 | 87 | Then, you can run `ganache-cli --fork YOUR_INFURA_URL_HERE`, or just `brownie run --network mainnet-fork` 88 | 89 | 1. Get some WETH, borrow, and repay! 90 | 91 | ``` 92 | brownie run scripts/aave/aave_borrow.py 93 | ``` 94 | 95 | Or, to test short selling: 96 | 97 | ``` 98 | brownie run scripts/swap/short_sell.py 99 | ``` 100 | -------------------------------------------------------------------------------- /brownie-config.yaml: -------------------------------------------------------------------------------- 1 | # exclude SafeMath when calculating test coverage 2 | # https://eth-brownie.readthedocs.io/en/v1.10.3/config.html#exclude_paths 3 | reports: 4 | exclude_contracts: 5 | - SafeMath 6 | dependencies: 7 | - smartcontractkit/chainlink-brownie-contracts@1.0.2 8 | - aave/protocol-v2@1.0.1 9 | - OpenZeppelin/openzeppelin-contracts@3.4.0 10 | compiler: 11 | solc: 12 | remappings: 13 | - '@chainlink=smartcontractkit/chainlink-brownie-contracts@1.0.2' 14 | - '@aave=aave/protocol-v2@1.0.1' 15 | - '@openzeppelin=OpenZeppelin/openzeppelin-contracts@3.4.0' 16 | # automatically fetch contract sources from Etherscan 17 | autofetch_sources: True 18 | dotenv: .env 19 | # Change to trye to verify smart contracts 20 | verify: False 21 | # set a custom mnemonic for the development network 22 | networks: 23 | default: mainnet-fork 24 | mainnet-fork: 25 | lending_pool_addresses_provider: '0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5' 26 | weth_token: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' 27 | sushiswapv2_router02: '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F' 28 | uniswapv2_router02: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' 29 | # For mainnet, the link token is the same 30 | link_token: '0x514910771af9ca656af840dff83e8264ecf986ca' 31 | aave_link_token: '0x514910771af9ca656af840dff83e8264ecf986ca' 32 | aave_dai_token: '0x6b175474e89094c44da98b954eedeac495271d0f' 33 | dai_token: '0x6b175474e89094c44da98b954eedeac495271d0f' 34 | link_eth_price_feed: '0xDC530D9457755926550b59e8ECcdaE7624181557' 35 | dai_eth_price_feed: '0x773616E4d11A78F511299002da57A0a94577F1f4' 36 | kovan: 37 | vrf_coordinator: '0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9' 38 | # https://aave.github.io/aave-addresses/kovan.json 39 | # Aave uses their own testnet tokens to ensure they are good 40 | # find the most up to date in the above 41 | aave_link_token: '0xAD5ce863aE3E4E9394Ab43d4ba0D80f419F61789' 42 | aave_dai_token: '0xFf795577d9AC8bD7D90Ee22b6C1703490b6512FD' 43 | link_token: '0xa36085F69e2889c224210F603D836748e7dC0088' 44 | keyhash: '0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4' 45 | fee: 100000000000000000 46 | oracle: '0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e' 47 | jobId: '29fa9aa13bf1468788b7cc4a500a45b8' 48 | eth_usd_price_feed: '0x9326BFA02ADD2366b30bacB125260Af641031331' 49 | link_eth_price_feed: '0x3Af8C569ab77af5230596Acf0E8c2F9351d24C38' 50 | dai_eth_price_feed: '0x22B58f1EbEDfCA50feF632bD73368b2FdA96D541' 51 | # You can get the address of the lending pool from the lending pool address provider 52 | # Sometimes the lending_pool address changes, so we want to get the address from there 53 | lending_pool_addresses_provider: '0x88757f2f99175387ab4c6a4b3067c77a695b0349' 54 | weth_token: '0xd0a1e359811322d97991e03f863a0c30c2cf029c' 55 | wallets: 56 | from_key: ${PRIVATE_KEY} 57 | from_mnemonic: ${MNEMONIC} 58 | 59 | # could also do from_mnemonic, and you'd have to change the accounts.add to accounts.from_mnemonic 60 | -------------------------------------------------------------------------------- /chainlink_mapping.py: -------------------------------------------------------------------------------- 1 | from brownie import config, network 2 | 3 | price_feed_mapping = { 4 | "mainnet-fork": { 5 | ( 6 | config["networks"][network.show_active()]["aave_dai_token"], 7 | config["networks"][network.show_active()]["weth_token"], 8 | ): "0x773616E4D11A78F511299002DA57A0A94577F1F4" 9 | } 10 | } 11 | # 0.000519450000000000 12 | -------------------------------------------------------------------------------- /img/aave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PatrickAlphaC/defi_py_mix/f59154b50ade1f34541614713dbd0f21e9fc1e36/img/aave.png -------------------------------------------------------------------------------- /img/python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PatrickAlphaC/defi_py_mix/f59154b50ade1f34541614713dbd0f21e9fc1e36/img/python.png -------------------------------------------------------------------------------- /interfaces/Aave/ILendingPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity 0.6.12; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import {ILendingPoolAddressesProvider} from '@aave/contracts/interfaces/ILendingPoolAddressesProvider.sol'; 6 | import {DataTypes} from '@aave/contracts/protocol/libraries/types/DataTypes.sol'; 7 | 8 | interface ILendingPool { 9 | /** 10 | * @dev Emitted on deposit() 11 | * @param reserve The address of the underlying asset of the reserve 12 | * @param user The address initiating the deposit 13 | * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens 14 | * @param amount The amount deposited 15 | * @param referral The referral code used 16 | **/ 17 | event Deposit( 18 | address indexed reserve, 19 | address user, 20 | address indexed onBehalfOf, 21 | uint256 amount, 22 | uint16 indexed referral 23 | ); 24 | 25 | /** 26 | * @dev Emitted on withdraw() 27 | * @param reserve The address of the underlyng asset being withdrawn 28 | * @param user The address initiating the withdrawal, owner of aTokens 29 | * @param to Address that will receive the underlying 30 | * @param amount The amount to be withdrawn 31 | **/ 32 | event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount); 33 | 34 | /** 35 | * @dev Emitted on borrow() and flashLoan() when debt needs to be opened 36 | * @param reserve The address of the underlying asset being borrowed 37 | * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just 38 | * initiator of the transaction on flashLoan() 39 | * @param onBehalfOf The address that will be getting the debt 40 | * @param amount The amount borrowed out 41 | * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable 42 | * @param borrowRate The numeric rate at which the user has borrowed 43 | * @param referral The referral code used 44 | **/ 45 | event Borrow( 46 | address indexed reserve, 47 | address user, 48 | address indexed onBehalfOf, 49 | uint256 amount, 50 | uint256 borrowRateMode, 51 | uint256 borrowRate, 52 | uint16 indexed referral 53 | ); 54 | 55 | /** 56 | * @dev Emitted on repay() 57 | * @param reserve The address of the underlying asset of the reserve 58 | * @param user The beneficiary of the repayment, getting his debt reduced 59 | * @param repayer The address of the user initiating the repay(), providing the funds 60 | * @param amount The amount repaid 61 | **/ 62 | event Repay( 63 | address indexed reserve, 64 | address indexed user, 65 | address indexed repayer, 66 | uint256 amount 67 | ); 68 | 69 | /** 70 | * @dev Emitted on swapBorrowRateMode() 71 | * @param reserve The address of the underlying asset of the reserve 72 | * @param user The address of the user swapping his rate mode 73 | * @param rateMode The rate mode that the user wants to swap to 74 | **/ 75 | event Swap(address indexed reserve, address indexed user, uint256 rateMode); 76 | 77 | /** 78 | * @dev Emitted on setUserUseReserveAsCollateral() 79 | * @param reserve The address of the underlying asset of the reserve 80 | * @param user The address of the user enabling the usage as collateral 81 | **/ 82 | event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user); 83 | 84 | /** 85 | * @dev Emitted on setUserUseReserveAsCollateral() 86 | * @param reserve The address of the underlying asset of the reserve 87 | * @param user The address of the user enabling the usage as collateral 88 | **/ 89 | event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user); 90 | 91 | /** 92 | * @dev Emitted on rebalanceStableBorrowRate() 93 | * @param reserve The address of the underlying asset of the reserve 94 | * @param user The address of the user for which the rebalance has been executed 95 | **/ 96 | event RebalanceStableBorrowRate(address indexed reserve, address indexed user); 97 | 98 | /** 99 | * @dev Emitted on flashLoan() 100 | * @param target The address of the flash loan receiver contract 101 | * @param initiator The address initiating the flash loan 102 | * @param asset The address of the asset being flash borrowed 103 | * @param amount The amount flash borrowed 104 | * @param premium The fee flash borrowed 105 | * @param referralCode The referral code used 106 | **/ 107 | event FlashLoan( 108 | address indexed target, 109 | address indexed initiator, 110 | address indexed asset, 111 | uint256 amount, 112 | uint256 premium, 113 | uint16 referralCode 114 | ); 115 | 116 | /** 117 | * @dev Emitted when the pause is triggered. 118 | */ 119 | event Paused(); 120 | 121 | /** 122 | * @dev Emitted when the pause is lifted. 123 | */ 124 | event Unpaused(); 125 | 126 | /** 127 | * @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via 128 | * LendingPoolCollateral manager using a DELEGATECALL 129 | * This allows to have the events in the generated ABI for LendingPool. 130 | * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation 131 | * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation 132 | * @param user The address of the borrower getting liquidated 133 | * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover 134 | * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator 135 | * @param liquidator The address of the liquidator 136 | * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants 137 | * to receive the underlying collateral asset directly 138 | **/ 139 | event LiquidationCall( 140 | address indexed collateralAsset, 141 | address indexed debtAsset, 142 | address indexed user, 143 | uint256 debtToCover, 144 | uint256 liquidatedCollateralAmount, 145 | address liquidator, 146 | bool receiveAToken 147 | ); 148 | 149 | /** 150 | * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared 151 | * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal, 152 | * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it 153 | * gets added to the LendingPool ABI 154 | * @param reserve The address of the underlying asset of the reserve 155 | * @param liquidityRate The new liquidity rate 156 | * @param stableBorrowRate The new stable borrow rate 157 | * @param variableBorrowRate The new variable borrow rate 158 | * @param liquidityIndex The new liquidity index 159 | * @param variableBorrowIndex The new variable borrow index 160 | **/ 161 | event ReserveDataUpdated( 162 | address indexed reserve, 163 | uint256 liquidityRate, 164 | uint256 stableBorrowRate, 165 | uint256 variableBorrowRate, 166 | uint256 liquidityIndex, 167 | uint256 variableBorrowIndex 168 | ); 169 | 170 | /** 171 | * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. 172 | * - E.g. User deposits 100 USDC and gets in return 100 aUSDC 173 | * @param asset The address of the underlying asset to deposit 174 | * @param amount The amount to be deposited 175 | * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user 176 | * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens 177 | * is a different wallet 178 | * @param referralCode Code used to register the integrator originating the operation, for potential rewards. 179 | * 0 if the action is executed directly by the user, without any middle-man 180 | **/ 181 | function deposit( 182 | address asset, 183 | uint256 amount, 184 | address onBehalfOf, 185 | uint16 referralCode 186 | ) external; 187 | 188 | /** 189 | * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned 190 | * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC 191 | * @param asset The address of the underlying asset to withdraw 192 | * @param amount The underlying amount to be withdrawn 193 | * - Send the value type(uint256).max in order to withdraw the whole aToken balance 194 | * @param to Address that will receive the underlying, same as msg.sender if the user 195 | * wants to receive it on his own wallet, or a different address if the beneficiary is a 196 | * different wallet 197 | * @return The final amount withdrawn 198 | **/ 199 | function withdraw( 200 | address asset, 201 | uint256 amount, 202 | address to 203 | ) external returns (uint256); 204 | 205 | /** 206 | * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower 207 | * already deposited enough collateral, or he was given enough allowance by a credit delegator on the 208 | * corresponding debt token (StableDebtToken or VariableDebtToken) 209 | * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet 210 | * and 100 stable/variable debt tokens, depending on the `interestRateMode` 211 | * @param asset The address of the underlying asset to borrow 212 | * @param amount The amount to be borrowed 213 | * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable 214 | * @param referralCode Code used to register the integrator originating the operation, for potential rewards. 215 | * 0 if the action is executed directly by the user, without any middle-man 216 | * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself 217 | * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator 218 | * if he has been given credit delegation allowance 219 | **/ 220 | function borrow( 221 | address asset, 222 | uint256 amount, 223 | uint256 interestRateMode, 224 | uint16 referralCode, 225 | address onBehalfOf 226 | ) external; 227 | 228 | /** 229 | * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned 230 | * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address 231 | * @param asset The address of the borrowed underlying asset previously borrowed 232 | * @param amount The amount to repay 233 | * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` 234 | * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable 235 | * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the 236 | * user calling the function if he wants to reduce/remove his own debt, or the address of any other 237 | * other borrower whose debt should be removed 238 | * @return The final amount repaid 239 | **/ 240 | function repay( 241 | address asset, 242 | uint256 amount, 243 | uint256 rateMode, 244 | address onBehalfOf 245 | ) external returns (uint256); 246 | 247 | /** 248 | * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa 249 | * @param asset The address of the underlying asset borrowed 250 | * @param rateMode The rate mode that the user wants to swap to 251 | **/ 252 | function swapBorrowRateMode(address asset, uint256 rateMode) external; 253 | 254 | /** 255 | * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve. 256 | * - Users can be rebalanced if the following conditions are satisfied: 257 | * 1. Usage ratio is above 95% 258 | * 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been 259 | * borrowed at a stable rate and depositors are not earning enough 260 | * @param asset The address of the underlying asset borrowed 261 | * @param user The address of the user to be rebalanced 262 | **/ 263 | function rebalanceStableBorrowRate(address asset, address user) external; 264 | 265 | /** 266 | * @dev Allows depositors to enable/disable a specific deposited asset as collateral 267 | * @param asset The address of the underlying asset deposited 268 | * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise 269 | **/ 270 | function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external; 271 | 272 | /** 273 | * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 274 | * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives 275 | * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk 276 | * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation 277 | * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation 278 | * @param user The address of the borrower getting liquidated 279 | * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover 280 | * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants 281 | * to receive the underlying collateral asset directly 282 | **/ 283 | function liquidationCall( 284 | address collateralAsset, 285 | address debtAsset, 286 | address user, 287 | uint256 debtToCover, 288 | bool receiveAToken 289 | ) external; 290 | 291 | /** 292 | * @dev Allows smartcontracts to access the liquidity of the pool within one transaction, 293 | * as long as the amount taken plus a fee is returned. 294 | * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. 295 | * For further details please visit https://developers.aave.com 296 | * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface 297 | * @param assets The addresses of the assets being flash-borrowed 298 | * @param amounts The amounts amounts being flash-borrowed 299 | * @param modes Types of the debt to open if the flash loan is not returned: 300 | * 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver 301 | * 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address 302 | * 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address 303 | * @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2 304 | * @param params Variadic packed params to pass to the receiver as extra information 305 | * @param referralCode Code used to register the integrator originating the operation, for potential rewards. 306 | * 0 if the action is executed directly by the user, without any middle-man 307 | **/ 308 | function flashLoan( 309 | address receiverAddress, 310 | address[] calldata assets, 311 | uint256[] calldata amounts, 312 | uint256[] calldata modes, 313 | address onBehalfOf, 314 | bytes calldata params, 315 | uint16 referralCode 316 | ) external; 317 | 318 | /** 319 | * @dev Returns the user account data across all the reserves 320 | * @param user The address of the user 321 | * @return totalCollateralETH the total collateral in ETH of the user 322 | * @return totalDebtETH the total debt in ETH of the user 323 | * @return availableBorrowsETH the borrowing power left of the user 324 | * @return currentLiquidationThreshold the liquidation threshold of the user 325 | * @return ltv the loan to value of the user 326 | * @return healthFactor the current health factor of the user 327 | **/ 328 | function getUserAccountData(address user) 329 | external 330 | view 331 | returns ( 332 | uint256 totalCollateralETH, 333 | uint256 totalDebtETH, 334 | uint256 availableBorrowsETH, 335 | uint256 currentLiquidationThreshold, 336 | uint256 ltv, 337 | uint256 healthFactor 338 | ); 339 | 340 | function initReserve( 341 | address reserve, 342 | address aTokenAddress, 343 | address stableDebtAddress, 344 | address variableDebtAddress, 345 | address interestRateStrategyAddress 346 | ) external; 347 | 348 | function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress) 349 | external; 350 | 351 | function setConfiguration(address reserve, uint256 configuration) external; 352 | 353 | /** 354 | * @dev Returns the configuration of the reserve 355 | * @param asset The address of the underlying asset of the reserve 356 | * @return The configuration of the reserve 357 | **/ 358 | function getConfiguration(address asset) 359 | external 360 | view 361 | returns (DataTypes.ReserveConfigurationMap memory); 362 | 363 | /** 364 | * @dev Returns the configuration of the user across all the reserves 365 | * @param user The user address 366 | * @return The configuration of the user 367 | **/ 368 | function getUserConfiguration(address user) 369 | external 370 | view 371 | returns (DataTypes.UserConfigurationMap memory); 372 | 373 | /** 374 | * @dev Returns the normalized income normalized income of the reserve 375 | * @param asset The address of the underlying asset of the reserve 376 | * @return The reserve's normalized income 377 | */ 378 | function getReserveNormalizedIncome(address asset) external view returns (uint256); 379 | 380 | /** 381 | * @dev Returns the normalized variable debt per unit of asset 382 | * @param asset The address of the underlying asset of the reserve 383 | * @return The reserve normalized variable debt 384 | */ 385 | function getReserveNormalizedVariableDebt(address asset) external view returns (uint256); 386 | 387 | /** 388 | * @dev Returns the state and configuration of the reserve 389 | * @param asset The address of the underlying asset of the reserve 390 | * @return The state of the reserve 391 | **/ 392 | function getReserveData(address asset) external view returns (DataTypes.ReserveData memory); 393 | 394 | function finalizeTransfer( 395 | address asset, 396 | address from, 397 | address to, 398 | uint256 amount, 399 | uint256 balanceFromAfter, 400 | uint256 balanceToBefore 401 | ) external; 402 | 403 | function getReservesList() external view returns (address[] memory); 404 | 405 | function getAddressesProvider() external view returns (ILendingPoolAddressesProvider); 406 | 407 | function setPause(bool val) external; 408 | 409 | function paused() external view returns (bool); 410 | } 411 | 412 | -------------------------------------------------------------------------------- /interfaces/Aave/ILendingPoolAddressesProvider.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity 0.6.12; 3 | 4 | /** 5 | * @title LendingPoolAddressesProvider contract 6 | * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles 7 | * - Acting also as factory of proxies and admin of those, so with right to change its implementations 8 | * - Owned by the Aave Governance 9 | * @author Aave 10 | **/ 11 | interface ILendingPoolAddressesProvider { 12 | event MarketIdSet(string newMarketId); 13 | event LendingPoolUpdated(address indexed newAddress); 14 | event ConfigurationAdminUpdated(address indexed newAddress); 15 | event EmergencyAdminUpdated(address indexed newAddress); 16 | event LendingPoolConfiguratorUpdated(address indexed newAddress); 17 | event LendingPoolCollateralManagerUpdated(address indexed newAddress); 18 | event PriceOracleUpdated(address indexed newAddress); 19 | event LendingRateOracleUpdated(address indexed newAddress); 20 | event ProxyCreated(bytes32 id, address indexed newAddress); 21 | event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy); 22 | 23 | function getMarketId() external view returns (string memory); 24 | 25 | function setMarketId(string calldata marketId) external; 26 | 27 | function setAddress(bytes32 id, address newAddress) external; 28 | 29 | function setAddressAsProxy(bytes32 id, address impl) external; 30 | 31 | function getAddress(bytes32 id) external view returns (address); 32 | 33 | function getLendingPool() external view returns (address); 34 | 35 | function setLendingPoolImpl(address pool) external; 36 | 37 | function getLendingPoolConfigurator() external view returns (address); 38 | 39 | function setLendingPoolConfiguratorImpl(address configurator) external; 40 | 41 | function getLendingPoolCollateralManager() external view returns (address); 42 | 43 | function setLendingPoolCollateralManager(address manager) external; 44 | 45 | function getPoolAdmin() external view returns (address); 46 | 47 | function setPoolAdmin(address admin) external; 48 | 49 | function getEmergencyAdmin() external view returns (address); 50 | 51 | function setEmergencyAdmin(address admin) external; 52 | 53 | function getPriceOracle() external view returns (address); 54 | 55 | function setPriceOracle(address priceOracle) external; 56 | 57 | function getLendingRateOracle() external view returns (address); 58 | 59 | function setLendingRateOracle(address lendingRateOracle) external; 60 | } 61 | -------------------------------------------------------------------------------- /interfaces/Chainlink/AggregatorV3Interface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.0; 3 | 4 | interface AggregatorV3Interface { 5 | 6 | function decimals() external view returns (uint8); 7 | function description() external view returns (string memory); 8 | function version() external view returns (uint256); 9 | 10 | // getRoundData and latestRoundData should both raise "No data present" 11 | // if they do not have data to report, instead of returning unset values 12 | // which could be misinterpreted as actual reported values. 13 | function getRoundData(uint80 _roundId) 14 | external 15 | view 16 | returns ( 17 | uint80 roundId, 18 | int256 answer, 19 | uint256 startedAt, 20 | uint256 updatedAt, 21 | uint80 answeredInRound 22 | ); 23 | function latestRoundData() 24 | external 25 | view 26 | returns ( 27 | uint80 roundId, 28 | int256 answer, 29 | uint256 startedAt, 30 | uint256 updatedAt, 31 | uint80 answeredInRound 32 | ); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /interfaces/Chainlink/LinkTokenInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.6; 2 | 3 | interface LinkTokenInterface { 4 | function allowance(address owner, address spender) external view returns (uint256 remaining); 5 | function approve(address spender, uint256 value) external returns (bool success); 6 | function balanceOf(address owner) external view returns (uint256 balance); 7 | function decimals() external view returns (uint8 decimalPlaces); 8 | function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); 9 | function increaseApproval(address spender, uint256 subtractedValue) external; 10 | function name() external view returns (string memory tokenName); 11 | function symbol() external view returns (string memory tokenSymbol); 12 | function totalSupply() external view returns (uint256 totalTokensIssued); 13 | function transfer(address to, uint256 value) external returns (bool success); 14 | function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success); 15 | function transferFrom(address from, address to, uint256 value) external returns (bool success); 16 | } -------------------------------------------------------------------------------- /interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.6; 2 | 3 | interface IERC20 { 4 | function allowance(address owner, address spender) external view returns (uint256 remaining); 5 | function approve(address spender, uint256 value) external returns (bool success); 6 | function balanceOf(address owner) external view returns (uint256 balance); 7 | function decimals() external view returns (uint8 decimalPlaces); 8 | function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); 9 | function increaseApproval(address spender, uint256 subtractedValue) external; 10 | function name() external view returns (string memory tokenName); 11 | function symbol() external view returns (string memory tokenSymbol); 12 | function totalSupply() external view returns (uint256 totalTokensIssued); 13 | function transfer(address to, uint256 value) external returns (bool success); 14 | function transferFrom(address from, address to, uint256 value) external returns (bool success); 15 | } 16 | -------------------------------------------------------------------------------- /interfaces/IWeth.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.4.19; 3 | 4 | interface IWeth { 5 | function allowance(address owner, address spender) external view returns (uint256 remaining); 6 | function approve(address spender, uint256 value) external returns (bool success); 7 | function balanceOf(address owner) external view returns (uint256 balance); 8 | function decimals() external view returns (uint8 decimalPlaces); 9 | function name() external view returns (string memory tokenName); 10 | function symbol() external view returns (string memory tokenSymbol); 11 | function totalSupply() external view returns (uint256 totalTokensIssued); 12 | function transfer(address to, uint256 value) external returns (bool success); 13 | function transferFrom(address from, address to, uint256 value) external returns (bool success); 14 | function deposit() external; 15 | } 16 | -------------------------------------------------------------------------------- /interfaces/Swap/IUniswapV2Pair.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IUniswapV2Pair { 4 | event Approval(address indexed owner, address indexed spender, uint value); 5 | event Transfer(address indexed from, address indexed to, uint value); 6 | 7 | function name() external pure returns (string memory); 8 | function symbol() external pure returns (string memory); 9 | function decimals() external pure returns (uint8); 10 | function totalSupply() external view returns (uint); 11 | function balanceOf(address owner) external view returns (uint); 12 | function allowance(address owner, address spender) external view returns (uint); 13 | 14 | function approve(address spender, uint value) external returns (bool); 15 | function transfer(address to, uint value) external returns (bool); 16 | function transferFrom(address from, address to, uint value) external returns (bool); 17 | 18 | function DOMAIN_SEPARATOR() external view returns (bytes32); 19 | function PERMIT_TYPEHASH() external pure returns (bytes32); 20 | function nonces(address owner) external view returns (uint); 21 | 22 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; 23 | 24 | event Mint(address indexed sender, uint amount0, uint amount1); 25 | event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); 26 | event Swap( 27 | address indexed sender, 28 | uint amount0In, 29 | uint amount1In, 30 | uint amount0Out, 31 | uint amount1Out, 32 | address indexed to 33 | ); 34 | event Sync(uint112 reserve0, uint112 reserve1); 35 | 36 | function MINIMUM_LIQUIDITY() external pure returns (uint); 37 | function factory() external view returns (address); 38 | function token0() external view returns (address); 39 | function token1() external view returns (address); 40 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 41 | function price0CumulativeLast() external view returns (uint); 42 | function price1CumulativeLast() external view returns (uint); 43 | function kLast() external view returns (uint); 44 | 45 | function mint(address to) external returns (uint liquidity); 46 | function burn(address to) external returns (uint amount0, uint amount1); 47 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; 48 | function skim(address to) external; 49 | function sync() external; 50 | 51 | function initialize(address, address) external; 52 | } 53 | -------------------------------------------------------------------------------- /interfaces/Swap/IUniswapV2Router02.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.6.2; 2 | 3 | interface IUniswapV2Router01 { 4 | function factory() external pure returns (address); 5 | function WETH() external pure returns (address); 6 | 7 | function addLiquidity( 8 | address tokenA, 9 | address tokenB, 10 | uint amountADesired, 11 | uint amountBDesired, 12 | uint amountAMin, 13 | uint amountBMin, 14 | address to, 15 | uint deadline 16 | ) external returns (uint amountA, uint amountB, uint liquidity); 17 | function addLiquidityETH( 18 | address token, 19 | uint amountTokenDesired, 20 | uint amountTokenMin, 21 | uint amountETHMin, 22 | address to, 23 | uint deadline 24 | ) external payable returns (uint amountToken, uint amountETH, uint liquidity); 25 | function removeLiquidity( 26 | address tokenA, 27 | address tokenB, 28 | uint liquidity, 29 | uint amountAMin, 30 | uint amountBMin, 31 | address to, 32 | uint deadline 33 | ) external returns (uint amountA, uint amountB); 34 | function removeLiquidityETH( 35 | address token, 36 | uint liquidity, 37 | uint amountTokenMin, 38 | uint amountETHMin, 39 | address to, 40 | uint deadline 41 | ) external returns (uint amountToken, uint amountETH); 42 | function removeLiquidityWithPermit( 43 | address tokenA, 44 | address tokenB, 45 | uint liquidity, 46 | uint amountAMin, 47 | uint amountBMin, 48 | address to, 49 | uint deadline, 50 | bool approveMax, uint8 v, bytes32 r, bytes32 s 51 | ) external returns (uint amountA, uint amountB); 52 | function removeLiquidityETHWithPermit( 53 | address token, 54 | uint liquidity, 55 | uint amountTokenMin, 56 | uint amountETHMin, 57 | address to, 58 | uint deadline, 59 | bool approveMax, uint8 v, bytes32 r, bytes32 s 60 | ) external returns (uint amountToken, uint amountETH); 61 | function swapExactTokensForTokens( 62 | uint amountIn, 63 | uint amountOutMin, 64 | address[] calldata path, 65 | address to, 66 | uint deadline 67 | ) external returns (uint[] memory amounts); 68 | function swapTokensForExactTokens( 69 | uint amountOut, 70 | uint amountInMax, 71 | address[] calldata path, 72 | address to, 73 | uint deadline 74 | ) external returns (uint[] memory amounts); 75 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) 76 | external 77 | payable 78 | returns (uint[] memory amounts); 79 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) 80 | external 81 | returns (uint[] memory amounts); 82 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) 83 | external 84 | returns (uint[] memory amounts); 85 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) 86 | external 87 | payable 88 | returns (uint[] memory amounts); 89 | 90 | function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); 91 | function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); 92 | function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); 93 | function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); 94 | function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); 95 | } 96 | 97 | interface IUniswapV2Router02 is IUniswapV2Router01 { 98 | function removeLiquidityETHSupportingFeeOnTransferTokens( 99 | address token, 100 | uint liquidity, 101 | uint amountTokenMin, 102 | uint amountETHMin, 103 | address to, 104 | uint deadline 105 | ) external returns (uint amountETH); 106 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( 107 | address token, 108 | uint liquidity, 109 | uint amountTokenMin, 110 | uint amountETHMin, 111 | address to, 112 | uint deadline, 113 | bool approveMax, uint8 v, bytes32 r, bytes32 s 114 | ) external returns (uint amountETH); 115 | 116 | function swapExactTokensForTokensSupportingFeeOnTransferTokens( 117 | uint amountIn, 118 | uint amountOutMin, 119 | address[] calldata path, 120 | address to, 121 | uint deadline 122 | ) external; 123 | function swapExactETHForTokensSupportingFeeOnTransferTokens( 124 | uint amountOutMin, 125 | address[] calldata path, 126 | address to, 127 | uint deadline 128 | ) external payable; 129 | function swapExactTokensForETHSupportingFeeOnTransferTokens( 130 | uint amountIn, 131 | uint amountOutMin, 132 | address[] calldata path, 133 | address to, 134 | uint deadline 135 | ) external; 136 | } 137 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | eth-brownie 2 | python-dotenv 3 | -------------------------------------------------------------------------------- /scripts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PatrickAlphaC/defi_py_mix/f59154b50ade1f34541614713dbd0f21e9fc1e36/scripts/__init__.py -------------------------------------------------------------------------------- /scripts/aave/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PatrickAlphaC/defi_py_mix/f59154b50ade1f34541614713dbd0f21e9fc1e36/scripts/aave/__init__.py -------------------------------------------------------------------------------- /scripts/aave/aave_borrow.py: -------------------------------------------------------------------------------- 1 | from brownie import config, interface, network 2 | from web3 import Web3 3 | from scripts.get_weth import get_weth 4 | from scripts.helpful_scripts import get_account, approve_erc20 5 | from scripts.chainlink.chainlink import get_asset_price 6 | 7 | amount = Web3.toWei(0.1, "ether") 8 | 9 | 10 | def main(): 11 | account = get_account() 12 | erc20_address = config["networks"][network.show_active()]["weth_token"] 13 | if network.show_active() in ["mainnet-fork"]: 14 | get_weth(account=account) 15 | lending_pool = get_lending_pool() 16 | approve_erc20(amount, lending_pool.address, erc20_address, account) 17 | print("Depositing...") 18 | lending_pool.deposit(erc20_address, amount, account.address, 0, {"from": account}) 19 | print("Deposited!") 20 | borrowable_eth, total_debt_eth = get_borrowable_data(lending_pool, account) 21 | print(f"LETS BORROW IT ALL") 22 | erc20_eth_price = get_asset_price() 23 | amount_erc20_to_borrow = (1 / erc20_eth_price) * (borrowable_eth * 0.95) 24 | print(f"We are going to borrow {amount_erc20_to_borrow} DAI") 25 | borrow_erc20(lending_pool, amount_erc20_to_borrow, account) 26 | 27 | borrowable_eth, total_debt_eth = get_borrowable_data(lending_pool, account) 28 | # amount_erc20_to_repay = (1 / erc20_eth_price) * (total_debt_eth * 0.95) 29 | repay_all(amount_erc20_to_borrow, lending_pool, account) 30 | 31 | 32 | def get_lending_pool(): 33 | lending_pool_addresses_provider = interface.ILendingPoolAddressesProvider( 34 | config["networks"][network.show_active()]["lending_pool_addresses_provider"] 35 | ) 36 | lending_pool_address = lending_pool_addresses_provider.getLendingPool() 37 | lending_pool = interface.ILendingPool(lending_pool_address) 38 | return lending_pool 39 | 40 | 41 | def get_borrowable_data(lending_pool, account): 42 | ( 43 | total_collateral_eth, 44 | total_debt_eth, 45 | available_borrow_eth, 46 | current_liquidation_threshold, 47 | tlv, 48 | health_factor, 49 | ) = lending_pool.getUserAccountData(account.address) 50 | available_borrow_eth = Web3.fromWei(available_borrow_eth, "ether") 51 | total_collateral_eth = Web3.fromWei(total_collateral_eth, "ether") 52 | total_debt_eth = Web3.fromWei(total_debt_eth, "ether") 53 | print(f"You have {total_collateral_eth} worth of ETH deposited.") 54 | print(f"You have {total_debt_eth} worth of ETH borrowed.") 55 | print(f"You can borrow {available_borrow_eth} worth of ETH.") 56 | return (float(available_borrow_eth), float(total_debt_eth)) 57 | 58 | 59 | def borrow_erc20(lending_pool, amount, account, erc20_address=None): 60 | erc20_address = ( 61 | erc20_address 62 | if erc20_address 63 | else config["networks"][network.show_active()]["aave_dai_token"] 64 | ) 65 | # 1 is stable interest rate 66 | # 0 is the referral code 67 | transaction = lending_pool.borrow( 68 | erc20_address, 69 | Web3.toWei(amount, "ether"), 70 | 1, 71 | 0, 72 | account.address, 73 | {"from": account}, 74 | ) 75 | transaction.wait(1) 76 | print(f"Congratulations! We have just borrowed {amount}") 77 | return transaction 78 | 79 | 80 | def repay_all(amount, lending_pool, account): 81 | approve_erc20( 82 | Web3.toWei(amount, "ether"), 83 | lending_pool, 84 | config["networks"][network.show_active()]["aave_dai_token"], 85 | account, 86 | ) 87 | tx = lending_pool.repay( 88 | config["networks"][network.show_active()]["aave_dai_token"], 89 | Web3.toWei(amount, "ether"), 90 | 1, 91 | account.address, 92 | {"from": account}, 93 | ) 94 | tx.wait(1) 95 | print("Repaid!") 96 | 97 | 98 | if __name__ == "__main__": 99 | main() 100 | -------------------------------------------------------------------------------- /scripts/chainlink/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PatrickAlphaC/defi_py_mix/f59154b50ade1f34541614713dbd0f21e9fc1e36/scripts/chainlink/__init__.py -------------------------------------------------------------------------------- /scripts/chainlink/chainlink.py: -------------------------------------------------------------------------------- 1 | from brownie import interface, network, config 2 | from web3 import Web3 3 | 4 | 5 | def get_asset_price( 6 | address_price_feed=None, 7 | ): 8 | # For mainnet we can just do: 9 | # return Contract(f"{pair}.data.eth").latestAnswer() / 1e8 10 | address_price_feed = ( 11 | address_price_feed 12 | if address_price_feed 13 | else config["networks"][network.show_active()]["dai_eth_price_feed"] 14 | ) 15 | dai_eth_price_feed = interface.AggregatorV3Interface(address_price_feed) 16 | latest_price = Web3.fromWei(dai_eth_price_feed.latestRoundData()[1], "ether") 17 | print(f"The DAI/ETH price is {latest_price}") 18 | return float(latest_price) 19 | -------------------------------------------------------------------------------- /scripts/get_weth.py: -------------------------------------------------------------------------------- 1 | from brownie import accounts, config, network, interface 2 | from scripts.helpful_scripts import get_account 3 | 4 | 5 | def main(): 6 | """ 7 | Runs the get_weth function to get WETH 8 | """ 9 | get_weth() 10 | 11 | 12 | def get_weth(account=None): 13 | """ 14 | Mints WETH by depositing ETH. 15 | """ 16 | account = ( 17 | account if account else get_account() 18 | ) # add your keystore ID as an argument to this call 19 | weth = interface.IWeth(config["networks"][network.show_active()]["weth_token"]) 20 | tx = weth.deposit({"from": account, "value": 1000000000000000000}) 21 | print("Received 1 WETH") 22 | return tx 23 | -------------------------------------------------------------------------------- /scripts/helpful_scripts.py: -------------------------------------------------------------------------------- 1 | from brownie import accounts, network, config, interface 2 | 3 | 4 | def get_account(index=None, id=None): 5 | if index: 6 | return accounts[index] 7 | if id: 8 | return accounts.load(id) 9 | if network.show_active() in ["hardhat", "development", "mainnet-fork"]: 10 | return accounts[0] 11 | if network.show_active() in config["networks"]: 12 | account = accounts.add(config["wallets"]["from_key"]) 13 | return account 14 | return None 15 | 16 | 17 | def approve_erc20(amount, to, erc20_address, account): 18 | print("Approving ERC20...") 19 | erc20 = interface.IERC20(erc20_address) 20 | tx_hash = erc20.approve(to, amount, {"from": account}) 21 | print("Approved!") 22 | tx_hash.wait(1) 23 | return tx_hash 24 | -------------------------------------------------------------------------------- /scripts/swap/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PatrickAlphaC/defi_py_mix/f59154b50ade1f34541614713dbd0f21e9fc1e36/scripts/swap/__init__.py -------------------------------------------------------------------------------- /scripts/swap/short_sell.py: -------------------------------------------------------------------------------- 1 | from brownie import accounts, config, interface, network 2 | from web3 import Web3 3 | from scripts.get_weth import get_weth 4 | from scripts.helpful_scripts import get_account, approve_erc20 5 | from chainlink_mapping import price_feed_mapping 6 | from scripts.swap.swap import swap 7 | from scripts.aave.aave_borrow import get_lending_pool, get_borrowable_data, borrow_erc20 8 | from scripts.chainlink.chainlink import get_asset_price 9 | 10 | amount = Web3.toWei(0.1, "ether") 11 | 12 | 13 | def main(): 14 | account = get_account() 15 | weth_address = config["networks"][network.show_active()]["weth_token"] 16 | dai_address = config["networks"][network.show_active()]["aave_dai_token"] 17 | sushiswapv2_router02 = config["networks"][network.show_active()][ 18 | "sushiswapv2_router02" 19 | ] 20 | if network.show_active() in ["mainnet-fork"]: 21 | get_weth(account=account) 22 | print( 23 | f"Starting WETH Balance is: {interface.IERC20(weth_address).balanceOf(account.address)}" 24 | ) 25 | print( 26 | f"Starting DAI Balance is: {interface.IERC20(dai_address).balanceOf(account.address)}" 27 | ) 28 | lending_pool = get_lending_pool() 29 | approve_erc20(amount, lending_pool.address, weth_address, account) 30 | print("Depositing...") 31 | lending_pool.deposit(weth_address, amount, account.address, 0, {"from": account}) 32 | print("Deposited!") 33 | borrowable_eth, total_debt_eth = get_borrowable_data(lending_pool, account) 34 | print("LETS BORROW IT ALL") 35 | dai_eth_price = get_asset_price() 36 | amount_dai_to_borrow = (1 / dai_eth_price) * (borrowable_eth * 0.95) 37 | print(f"We are going to borrow {amount_dai_to_borrow} DAI") 38 | borrow_erc20(lending_pool, amount_dai_to_borrow, account, erc20_address=dai_address) 39 | # Sell the borrowed asset 40 | # You could use anything like Aave, Uniswap, 1inch, etc 41 | amount_dai_to_borrow = int(amount_dai_to_borrow * (10 ** 18)) 42 | price_feed_address = price_feed_mapping[network.show_active()][ 43 | (dai_address, weth_address) 44 | ] 45 | 46 | tx = approve_erc20(amount_dai_to_borrow, sushiswapv2_router02, dai_address, account) 47 | tx.wait(1) 48 | 49 | swap( 50 | dai_address, 51 | weth_address, 52 | amount_dai_to_borrow - Web3.toWei(1, "ether"), 53 | account, 54 | price_feed_address, 55 | sushiswapv2_router02, 56 | ) 57 | print( 58 | f"Ending WETH Balance is: {interface.IERC20(weth_address).balanceOf(account.address)}" 59 | ) 60 | print( 61 | f"Ending DAI Balance is: {interface.IERC20(dai_address).balanceOf(account.address)}" 62 | ) 63 | # borrowable_eth, total_debt_eth = get_borrowable_data(lending_pool, account) 64 | -------------------------------------------------------------------------------- /scripts/swap/swap.py: -------------------------------------------------------------------------------- 1 | import brownie 2 | from brownie import accounts, config, interface, network, chain 3 | from web3 import Web3 4 | from scripts.get_weth import get_weth 5 | from scripts.helpful_scripts import get_account, approve_erc20 6 | from scripts.chainlink.chainlink import get_asset_price 7 | from chainlink_mapping import price_feed_mapping 8 | 9 | amount_to_swap = Web3.toWei(0.1, "ether") 10 | 11 | 12 | def main(): 13 | account = get_account() 14 | weth_address = config["networks"][network.show_active()]["weth_token"] 15 | dai_address = config["networks"][network.show_active()]["aave_dai_token"] 16 | sushiswap02_router02 = config["networks"][network.show_active()][ 17 | "sushiswapv2_router02" 18 | ] 19 | print( 20 | f"The starting balance of DAI in {account.address} is now {interface.IERC20(dai_address).balanceOf(account.address)}" 21 | ) 22 | if network.show_active() in ["mainnet-fork"]: 23 | get_weth(account=account) 24 | tx = approve_erc20(amount_to_swap, sushiswap02_router02, weth_address, account) 25 | tx.wait(1) 26 | price_feed_address = price_feed_mapping[network.show_active()][ 27 | (dai_address, weth_address) 28 | ] 29 | swap( 30 | weth_address, 31 | dai_address, 32 | amount_to_swap, 33 | account, 34 | price_feed_address, 35 | sushiswap02_router02, 36 | reverse_feed=True, 37 | ) 38 | print( 39 | f"The ending balance of DAI in {account.address} is now {interface.IERC20(dai_address).balanceOf(account.address)}" 40 | ) 41 | 42 | 43 | def swap( 44 | address_from_token, 45 | address_to_token, 46 | amount, 47 | account, 48 | price_feed_address, 49 | swap_router_address, 50 | reverse_feed=False, 51 | ): 52 | path = [ 53 | address_from_token, 54 | address_to_token, 55 | ] 56 | # The pool jumping path to swap your token 57 | from_to_price = get_asset_price(address_price_feed=price_feed_address) 58 | if reverse_feed: 59 | from_to_price = 1 / from_to_price 60 | # amountOutMin = int((from_to_price * 0.5) * 10 ** 18) 61 | # 98 is 2% slippage 62 | # I get a little weird with units here 63 | # from_to_price isn't in wei, but amount is 64 | amountOutMin = int((from_to_price * 0.90) * amount) 65 | timestamp = chain[brownie.web3.eth.get_block_number()]["timestamp"] + 120 66 | routerv2 = interface.IUniswapV2Router02(swap_router_address) 67 | swap_tx = routerv2.swapExactTokensForTokens( 68 | amount, amountOutMin, path, account.address, timestamp, {"from": account} 69 | ) 70 | swap_tx.wait(1) 71 | return swap_tx 72 | 73 | 74 | # Starting ETH Balance is: 1000000000000000000 75 | # Starting DAI Balance is: 0 76 | # Ending ETH Balance is: 975655842557031085 77 | # Ending DAI Balance is: 10480 78 | -------------------------------------------------------------------------------- /tests/test_aave_borrow.py: -------------------------------------------------------------------------------- 1 | from scripts.aave.aave_borrow import ( 2 | get_lending_pool, 3 | approve_erc20, 4 | ) 5 | from scripts.chainlink.chainlink import get_asset_price 6 | from scripts.helpful_scripts import get_account 7 | from brownie import config, network 8 | 9 | 10 | def test_get_asset_price(): 11 | # Arrange / Act 12 | asset_price = get_asset_price() 13 | # Assert 14 | assert asset_price > 0 15 | 16 | 17 | def test_get_lending_pool(): 18 | # Arrange / Act 19 | lending_pool = get_lending_pool() 20 | # Assert 21 | assert lending_pool != None 22 | 23 | 24 | def test_approve_erc20(): 25 | # Arrange 26 | account = get_account() 27 | lending_pool = get_lending_pool() 28 | amount = 1000000000000000000 # 1 29 | erc20_address = config["networks"][network.show_active()]["weth_token"] 30 | # Act 31 | approved = approve_erc20(amount, lending_pool.address, erc20_address, account) 32 | # Assert 33 | assert approved is True 34 | -------------------------------------------------------------------------------- /tests/test_short_sell.py: -------------------------------------------------------------------------------- 1 | # from scripts.aave.aave_borrow import ( 2 | # get_asset_price, 3 | # get_lending_pool, 4 | # approve_erc20, 5 | # get_account, 6 | # ) 7 | # from brownie import config, network 8 | 9 | 10 | # def test_get_asset_price(): 11 | # # Arrange / Act 12 | # asset_price = get_asset_price() 13 | # # Assert 14 | # assert asset_price > 0 15 | 16 | 17 | # def test_get_lending_pool(): 18 | # # Arrange / Act 19 | # lending_pool = get_lending_pool() 20 | # # Assert 21 | # assert lending_pool != None 22 | 23 | 24 | # def test_approve_erc20(): 25 | # # Arrange 26 | # account = get_account() 27 | # lending_pool = get_lending_pool() 28 | # amount = 1000000000000000000 # 1 29 | # erc20_address = config["networks"][network.show_active()]["weth_token"] 30 | # # Act 31 | # approved = approve_erc20(amount, lending_pool.address, erc20_address, account) 32 | # # Assert 33 | # assert approved is True 34 | --------------------------------------------------------------------------------