diff --git a/.env b/.env new file mode 100644 index 0000000..b45f8a8 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +export WEB3_INFURA_PROJECT_ID=YourProjectID +export ETHERSCAN_TOKEN=YourApiToken +export PRIVATE_KEY="0xasdfasdfasdfasd..." diff --git a/.gitignore b/.gitignore index a418c16..06721b5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ __pycache__ .hypothesis/ build/ reports/ +.env diff --git a/README.md b/README.md index 56cef55..8f34627 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This mix is configured for use with [Ganache](https://github.com/trufflesuite/ga 1. [Install Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html) & [Ganache-CLI](https://github.com/trufflesuite/ganache-cli), if you haven't already. -2. Sign up for [Infura](https://infura.io/) and generate an API key. Store it in the `WEB3_INFURA_PROJECT_ID` environment variable. +2. Sign up for [Infura](https://infura.io/) and generate an API key. Store it in the `WEB3_INFURA_PROJECT_ID` environment variable. You can [learn more about environment variables here](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html). If you're unfamiliar with environment variables you can just add all these commands to your `.env` file and run `source .env` when you're done. ```bash export WEB3_INFURA_PROJECT_ID=YourProjectID @@ -32,7 +32,33 @@ export ETHERSCAN_TOKEN=YourApiToken brownie bake aave-flashloan ``` -## Basic Use +5. Add your `PRIVATE_KEY` environment variable, with [a private key from you wallet.](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key). *Note: If using metamask, you'll have to add a `0x` to the start of your private key) + +## Quickstart (Kovan) + +We can see our flash loans on Etherscan via the Kovan testnet. If you're rather *run everything locally, check out the [Basic Console Use](#basic-console-use). + +1. Get some WETH. We need this to pay the preimum that flash loans cost. + +```bash +$ brownie run scripts/get_weth.py --network kovan +``` + +2. Deploy the flash loan contract. This will also fund the contract with WETH to pay the flash loan fee if it's not funded. + +```bash +$ brownie run scripts/deployment_v2.py --network kovan +``` + +3. Execute the flash loan + +```bash +$ brownie run scripts/run_flash_loan_v2.py --network kovan +``` + +This will print out an etherscan URL to see the flash loan transaction. [Like this one.](https://kovan.etherscan.io/tx/0x161d423dd1a56e7c440dabed95bea314b63668fc462567348ba4dd188e894de3) + +## Basic Console Use To perform a simple flash loan in a development environment: @@ -85,8 +111,8 @@ Transaction sent: 0x335530e6d2b7588ee4727b35ae1ed8634a264aca04b325640101ec1c2b89 [`contracts/v2/FlashloanV2.sol`](contracts/v2/FlashloanV2.sol) is where you implement your own logic for flash loans. In particular: -* The size of the loan is set in line 39 in `flashloan`. -* Custom flash loan logic is added after line 23 in `executeOperation`. +* The size of the loan is set in line 89 in `flashloan`. +* Custom flash loan logic is added after line 31 in `executeOperation`. See the Aave documentation on [Performing a Flash Loan](https://docs.aave.com/developers/guides/flash-loans) for more detailed information. @@ -253,16 +279,13 @@ See the [Brownie documentation](https://eth-brownie.readthedocs.io/en/stable/cor When you are finished testing and ready to deploy to the mainnet: -1. [Import a keystore](https://eth-brownie.readthedocs.io/en/stable/account-management.html#importing-from-a-private-key) into Brownie for the account you wish to deploy from. -2. Edit [`scripts/deployment.py`](scripts/deployment.py) and add your keystore ID according to the comments. -3. Run the deployment script on the mainnet using the following command: +1. [Import a keystore](https://eth-brownie.readthedocs.io/en/stable/account-management.html#importing-from-a-private-key) into Brownie for the account you wish to deploy from. Add this as a `PRIVATE_KEY` environment variable. +2. Run the deployment script on the mainnet using the following command: ```bash -$ brownie run deployment --network mainnet +$ brownie run scripts/deployment_v2.py --network mainnet ``` -You will be prompted to enter your keystore password, and then the contract will be deployed. - ## Known issues ### No access to archive state errors diff --git a/brownie-config.yaml b/brownie-config.yaml index c990a4c..0c80f42 100644 --- a/brownie-config.yaml +++ b/brownie-config.yaml @@ -14,3 +14,17 @@ compiler: solc: remappings: - "@openzeppelin=OpenZeppelin/openzeppelin-contracts@3.0.0" +networks: + default: mainnet-fork + mainnet-fork: + aave_lending_pool_v2: "0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5" + weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" + kovan: + aave_lending_pool_v2: "0x88757f2f99175387ab4c6a4b3067c77a695b0349" + weth: "0xd0a1e359811322d97991e03f863a0c30c2cf029c" + mainnet: + aave_lending_pool_v2: "0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5" + weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" +wallets: + from_key: ${PRIVATE_KEY} + from_mnemonic: ${MNEMONIC} diff --git a/contracts/v2/FlashloanV2.sol b/contracts/v2/FlashloanV2.sol index e03c3b3..5fc9d2e 100644 --- a/contracts/v2/FlashloanV2.sol +++ b/contracts/v2/FlashloanV2.sol @@ -29,7 +29,7 @@ contract FlashloanV2 is FlashLoanReceiverBaseV2, Withdrawable { override returns (bool) { - + // // This contract now has the funds requested. // Your logic goes here. @@ -96,4 +96,4 @@ contract FlashloanV2 is FlashLoanReceiverBaseV2, Withdrawable { _flashloan(assets, amounts); } -} +} \ No newline at end of file diff --git a/interfaces/WethInterface.sol b/interfaces/WethInterface.sol new file mode 100644 index 0000000..32a39c4 --- /dev/null +++ b/interfaces/WethInterface.sol @@ -0,0 +1,15 @@ + +pragma solidity ^0.4.19; + +interface WethInterface { + function allowance(address owner, address spender) external view returns (uint256 remaining); + function approve(address spender, uint256 value) external returns (bool success); + function balanceOf(address owner) external view returns (uint256 balance); + function decimals() external view returns (uint8 decimalPlaces); + function name() external view returns (string memory tokenName); + function symbol() external view returns (string memory tokenSymbol); + function totalSupply() external view returns (uint256 totalTokensIssued); + function transfer(address to, uint256 value) external returns (bool success); + function transferFrom(address from, address to, uint256 value) external returns (bool success); + function deposit() external; +} diff --git a/scripts/deployment_v2.py b/scripts/deployment_v2.py index 62ed3ce..cc82fd1 100644 --- a/scripts/deployment_v2.py +++ b/scripts/deployment_v2.py @@ -1,6 +1,6 @@ -from brownie import FlashloanV2, accounts +from brownie import FlashloanV2, accounts, config, network -AAVE_LENDING_POOL_ADDRESS_PROVIDER = "0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5" +# AAVE_LENDING_POOL_ADDRESS_PROVIDER = "0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5" def main(): @@ -8,7 +8,12 @@ def main(): Deploy a `FlashloanV2` contract from `accounts[0]`. """ - acct = accounts.load() # add your keystore ID as an argument to this call + acct = accounts.add( + config["wallets"]["from_key"] + ) # add your keystore ID as an argument to this call - flashloan = FlashloanV2.deploy(AAVE_LENDING_POOL_ADDRESS_PROVIDER, {"from": acct}) + flashloan = FlashloanV2.deploy( + config["networks"][network.show_active()]["aave_lending_pool_v2"], + {"from": acct}, + ) return flashloan diff --git a/scripts/get_weth.py b/scripts/get_weth.py new file mode 100644 index 0000000..ccf68e0 --- /dev/null +++ b/scripts/get_weth.py @@ -0,0 +1,21 @@ +from brownie import accounts, config, network, interface + + +def main(): + """ + Runs the get_weth function to get WETH + """ + get_weth() + + +def get_weth(): + """ + Mints WETH by depositing ETH. + """ + acct = accounts.add( + config["wallets"]["from_key"] + ) # add your keystore ID as an argument to this call + weth = interface.WethInterface(config["networks"][network.show_active()]["weth"]) + tx = weth.deposit({"from": acct, "value": 1000000000000000000}) + print("Received 1 WETH") + return tx diff --git a/scripts/run_flash_loan_v2.py b/scripts/run_flash_loan_v2.py new file mode 100644 index 0000000..1b61d2b --- /dev/null +++ b/scripts/run_flash_loan_v2.py @@ -0,0 +1,22 @@ +from brownie import FlashloanV2, accounts, config, network, interface + +MINIMUM_FLASHLOAN_WETH_BALANCE = 500000000000000000 +ETHERSCAN_TX_URL = "https://kovan.etherscan.io/tx/{}" + + +def main(): + """ + Executes the funcitonality of the flash loan. + """ + acct = accounts.add(config["wallets"]["from_key"]) + print("Getting Flashloan contract...") + flashloan = FlashloanV2[len(FlashloanV2) - 1] + weth = interface.WethInterface(config["networks"][network.show_active()]["weth"]) + # We need to fund it if it doesn't have any token to fund! + if weth.balanceOf(flashloan) < MINIMUM_FLASHLOAN_WETH_BALANCE: + print("Funding Flashloan contract with WETH...") + weth.transfer(flashloan, "1 ether", {"from": acct}) + print("Executing Flashloan...") + tx = flashloan.flashloan(weth, {"from": acct}) + print("You did it! View your tx here: " + ETHERSCAN_TX_URL.format(tx.txid)) + return flashloan