├── 1.txt ├── scripts ├── ERC20MockConstructorParams.js ├── ERC20.deploy.ts └── deployUtils.ts ├── .gitattributes ├── README.md ├── .gitignore ├── tsconfig.json ├── contracts ├── interface │ ├── CompoundInterface.sol │ └── compound.sol ├── ExternalStore.sol ├── CompoundController.sol └── YieldFarming.sol ├── package.json ├── hardhat.config.ts └── test ├── ABI ├── UniswapFactory.json └── UniswapRouter.json └── CompoundBorrow.ts /1.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/ERC20MockConstructorParams.js: -------------------------------------------------------------------------------- 1 | module.exports = ["100000", "MyToken", "MTK"] -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yield Farming 2 | - borrow from Compound 3 | - staking to Uniswap 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | typechain-types 7 | 8 | # Hardhat files 9 | cache 10 | artifacts 11 | .env 12 | 13 | -------------------------------------------------------------------------------- /scripts/ERC20.deploy.ts: -------------------------------------------------------------------------------- 1 | import { deploy } from "./deployUtils"; 2 | 3 | deploy("ERC20Mock", ["100000", "MyToken", "MTK"]).catch((error) => { 4 | console.error(error); 5 | process.exitCode = 1; 6 | }); 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "rootDirs": ["./src", "./scripts", "./test"], 10 | "resolveJsonModule": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interface/CompoundInterface.sol: -------------------------------------------------------------------------------- 1 | interface ICompound { 2 | function supply(address asset, address borrowAsset, uint256 amount) external; 3 | function borrow(address asset, address borrowAsset, uint256 amount) external; 4 | function repayBorrow(address asset, address borrowAsset, uint256 amount) external; 5 | function getCTokenbalance(address asset) external returns(uint256); 6 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nft-marketplace-auction", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/HugoBrunet13/NFT-Marketplace-Auction.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/HugoBrunet13/NFT-Marketplace-Auction/issues" 18 | }, 19 | "homepage": "https://github.com/HugoBrunet13/NFT-Marketplace-Auction#readme", 20 | "devDependencies": { 21 | "@nomicfoundation/hardhat-toolbox": "^2.0.2", 22 | "@openzeppelin/contracts": "^4.8.3", 23 | "dotenv": "^16.0.3", 24 | "hardhat": "^2.16.1", 25 | "ts-node": "^10.9.1", 26 | "typescript": "^5.0.4" 27 | }, 28 | "dependencies": { 29 | "@chainlink/contracts": "^0.6.1", 30 | "@compound-finance/compound-js": "^0.6.1", 31 | "@uniswap/sdk": "^3.0.3", 32 | "@uniswap/v2-core": "^1.0.1", 33 | "@uniswap/v2-periphery": "^1.1.0-beta.0", 34 | "compound": "^1.2.4", 35 | "uniswap": "^0.0.1", 36 | "uniswap-sdk": "^3.0.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /scripts/deployUtils.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | 3 | export async function deploy(contractName: string, params: any[]) { 4 | const ContractArtifact = await ethers.getContractFactory(contractName); 5 | console.log(params[0]); 6 | const gasPrice = await ContractArtifact.signer.getGasPrice(); 7 | console.log(`Current gas price: ${gasPrice}`); 8 | 9 | const estimatedGas = await ContractArtifact.signer.estimateGas( 10 | ContractArtifact.getDeployTransaction(...params) 11 | ); 12 | console.log( 13 | contractName + "contract deployment " + `estimated gas: ${estimatedGas}` 14 | ); 15 | 16 | const deploymentPrice = gasPrice.mul(estimatedGas); 17 | const deployerBalance = await ContractArtifact.signer.getBalance(); 18 | console.log( 19 | contractName + 20 | ` Deployment price: ${ethers.utils.formatEther(deploymentPrice)}` 21 | ); 22 | console.log( 23 | `Deployer balance: ${ethers.utils.formatEther(deployerBalance)}` 24 | ); 25 | 26 | if (deployerBalance.lt(deploymentPrice)) { 27 | throw new Error( 28 | `Insufficient funds. Top up your account balance by ${ethers.utils.formatEther( 29 | deploymentPrice.sub(deployerBalance) 30 | )}` 31 | ); 32 | } 33 | const Contract = await ContractArtifact.deploy(...params); 34 | 35 | await Contract.deployed(); 36 | 37 | console.log(contractName + `contract deployed to ${Contract.address}`); 38 | } 39 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from "hardhat/config"; 2 | import "@nomicfoundation/hardhat-toolbox"; 3 | import * as dotenv from 'dotenv'; 4 | 5 | dotenv.config(); 6 | 7 | // Go to https://infura.io, sign up, create a new API key 8 | // in its dashboard, and replace "KEY" with it 9 | //const INFURA_API_KEY: string | undefined = process.env.INFURA_API_KEY; 10 | 11 | // Replace this private key with your Sepolia account private key 12 | // To export your private key from Metamask, open Metamask and 13 | // go to Account Details > Export Private Key 14 | // Beware: NEVER put real Ether into testing accounts 15 | //const PRIVATE_KEY: string| undefined = process.env.WALLET_PRIVATE_KEY; 16 | const { INFURA_API_KEY, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env; 17 | 18 | 19 | const config: HardhatUserConfig = { 20 | solidity: "0.8.18", 21 | networks: { 22 | hardhat: { 23 | forking: { 24 | // url: "https://mainnet.infura.io/v3/9302aa8d88b44d37a7b45271ecaaeb25", 25 | url: "https://eth-mainnet.g.alchemy.com/v2/_6Ek2Lzp4UHqjrPb7cNAdRWZSJxiuEkv", 26 | }, 27 | allowUnlimitedContractSize: true 28 | }, 29 | localhost: { 30 | allowUnlimitedContractSize: true 31 | }, 32 | mumbai: { 33 | url: `https://rpc-mumbai.maticvigil.com`, 34 | accounts: [PRIVATE_KEY] 35 | } 36 | 37 | }, 38 | etherscan: { 39 | apiKey: ETHERSCAN_API_KEY, 40 | }, 41 | }; 42 | 43 | export default config; -------------------------------------------------------------------------------- /contracts/interface/compound.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8; 3 | 4 | interface CErc20 { 5 | function balanceOf(address) external view returns (uint); 6 | 7 | function mint(uint) external returns (uint); 8 | 9 | function exchangeRateCurrent() external returns (uint); 10 | 11 | function supplyRatePerBlock() external returns (uint); 12 | 13 | function balanceOfUnderlying(address) external returns (uint); 14 | 15 | function redeem(uint) external returns (uint); 16 | 17 | function redeemUnderlying(uint) external returns (uint); 18 | 19 | function borrow(uint) external returns (uint); 20 | 21 | function borrowBalanceCurrent(address) external returns (uint); 22 | 23 | function borrowRatePerBlock() external view returns (uint); 24 | 25 | function repayBorrow(uint) external returns (uint); 26 | 27 | function totalSupply() external returns (uint); 28 | 29 | function liquidateBorrow( 30 | address borrower, 31 | uint amount, 32 | address collateral 33 | ) external returns (uint); 34 | } 35 | 36 | interface CEth { 37 | function balanceOf(address) external view returns (uint); 38 | 39 | function mint() external payable; 40 | 41 | function exchangeRateCurrent() external returns (uint); 42 | 43 | function supplyRatePerBlock() external returns (uint); 44 | 45 | function balanceOfUnderlying(address) external returns (uint); 46 | 47 | function redeem(uint) external returns (uint); 48 | 49 | function redeemUnderlying(uint) external returns (uint); 50 | 51 | function borrow(uint) external returns (uint); 52 | 53 | function borrowBalanceCurrent(address) external returns (uint); 54 | 55 | function borrowRatePerBlock() external view returns (uint); 56 | 57 | function repayBorrow() external payable; 58 | } 59 | 60 | interface Comptroller { 61 | function markets(address) 62 | external 63 | view 64 | returns ( 65 | bool, 66 | uint, 67 | bool 68 | ); 69 | 70 | function enterMarkets(address[] calldata) external returns (uint[] memory); 71 | 72 | function getAccountLiquidity(address) 73 | external 74 | view 75 | returns ( 76 | uint, 77 | uint, 78 | uint 79 | ); 80 | 81 | function closeFactorMantissa() external view returns (uint); 82 | 83 | function liquidationIncentiveMantissa() external view returns (uint); 84 | 85 | function liquidateCalculateSeizeTokens( 86 | address cTokenBorrowed, 87 | address cTokenCollateral, 88 | uint actualRepayAmount 89 | ) external view returns (uint, uint); 90 | } 91 | 92 | interface PriceFeed { 93 | function getUnderlyingPrice(address cToken) external view returns (uint); 94 | } -------------------------------------------------------------------------------- /contracts/ExternalStore.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; 5 | import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; 8 | 9 | contract ExternalStore is ERC1155, Ownable { 10 | mapping(address => mapping(uint256 => uint256)) private _balances; 11 | mapping(address => mapping(address => bool)) private _operatorApprovals; 12 | constructor(string memory uri_)ERC1155(uri_) {} 13 | function wrap(address user, uint256 amount, uint256 id) external onlyOwner { 14 | _balances[user][id] += amount; 15 | emit TransferSingle(msg.sender, address(0), user, id, amount); 16 | } 17 | 18 | function burn(address user, uint256 id, uint256 amount) external onlyOwner { 19 | _balances[user][id] -= amount; 20 | emit TransferSingle(msg.sender, user, address(0), id, amount); 21 | } 22 | 23 | function balanceOf(address account, uint256 id) public view override returns (uint256) { 24 | return _balances[account][id]; 25 | } 26 | 27 | 28 | function balanceOfBatch(address[] memory accounts, uint256[] memory ids) public view override returns (uint256[] memory) { 29 | uint256[] memory batchBalances = new uint256[](accounts.length * ids.length); 30 | 31 | for (uint256 i = 0; i < accounts.length; i++) { 32 | for (uint256 j = 0; j < ids.length; j++) { 33 | batchBalances[i * ids.length + j] = _balances[accounts[i]][ids[j]]; 34 | } 35 | } 36 | 37 | return batchBalances; 38 | } 39 | 40 | function setApprovalForAll(address operator, bool approved) public override { 41 | require(msg.sender != operator, "ERC1155: setting approval status for self"); 42 | 43 | _operatorApprovals[msg.sender][operator] = approved; 44 | emit ApprovalForAll(msg.sender, operator, approved); 45 | } 46 | 47 | function isApprovedForAll(address account, address operator) public view override returns (bool) { 48 | return _operatorApprovals[account][operator]; 49 | } 50 | 51 | function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) public override { 52 | require(to != address(0), "ERC1155: transfer to the zero address"); 53 | 54 | address operator = msg.sender; 55 | require(from == msg.sender || isApprovedForAll(from, operator), "ERC1155: caller is not owner nor approved"); 56 | 57 | _balances[from][id] -= amount; 58 | _balances[to][id] += amount; 59 | 60 | emit TransferSingle(operator, from, to, id, amount); 61 | } 62 | 63 | function safeBatchTransferFrom(address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) public override { 64 | require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); 65 | require(to != address(0), "ERC1155: transfer to the zero address"); 66 | 67 | address operator = msg.sender; 68 | require(from == msg.sender || isApprovedForAll(from, operator), "ERC1155: caller is not owner nor approved"); 69 | 70 | for (uint256 i = 0; i < ids.length; i++) { 71 | _balances[from][ids[i]] -= amounts[i]; 72 | _balances[to][ids[i]] += amounts[i]; 73 | } 74 | 75 | emit TransferBatch(operator, from, to, ids, amounts); 76 | } 77 | } -------------------------------------------------------------------------------- /test/ABI/UniswapFactory.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_feeToSetter", 7 | "type": "address" 8 | } 9 | ], 10 | "payable": false, 11 | "stateMutability": "nonpayable", 12 | "type": "constructor" 13 | }, 14 | { 15 | "anonymous": false, 16 | "inputs": [ 17 | { 18 | "indexed": true, 19 | "internalType": "address", 20 | "name": "token0", 21 | "type": "address" 22 | }, 23 | { 24 | "indexed": true, 25 | "internalType": "address", 26 | "name": "token1", 27 | "type": "address" 28 | }, 29 | { 30 | "indexed": false, 31 | "internalType": "address", 32 | "name": "pair", 33 | "type": "address" 34 | }, 35 | { 36 | "indexed": false, 37 | "internalType": "uint256", 38 | "name": "", 39 | "type": "uint256" 40 | } 41 | ], 42 | "name": "PairCreated", 43 | "type": "event" 44 | }, 45 | { 46 | "constant": true, 47 | "inputs": [ 48 | { 49 | "internalType": "uint256", 50 | "name": "", 51 | "type": "uint256" 52 | } 53 | ], 54 | "name": "allPairs", 55 | "outputs": [ 56 | { 57 | "internalType": "address", 58 | "name": "", 59 | "type": "address" 60 | } 61 | ], 62 | "payable": false, 63 | "stateMutability": "view", 64 | "type": "function" 65 | }, 66 | { 67 | "constant": true, 68 | "inputs": [], 69 | "name": "allPairsLength", 70 | "outputs": [ 71 | { 72 | "internalType": "uint256", 73 | "name": "", 74 | "type": "uint256" 75 | } 76 | ], 77 | "payable": false, 78 | "stateMutability": "view", 79 | "type": "function" 80 | }, 81 | { 82 | "constant": false, 83 | "inputs": [ 84 | { 85 | "internalType": "address", 86 | "name": "tokenA", 87 | "type": "address" 88 | }, 89 | { 90 | "internalType": "address", 91 | "name": "tokenB", 92 | "type": "address" 93 | } 94 | ], 95 | "name": "createPair", 96 | "outputs": [ 97 | { 98 | "internalType": "address", 99 | "name": "pair", 100 | "type": "address" 101 | } 102 | ], 103 | "payable": false, 104 | "stateMutability": "nonpayable", 105 | "type": "function" 106 | }, 107 | { 108 | "constant": true, 109 | "inputs": [], 110 | "name": "feeTo", 111 | "outputs": [ 112 | { 113 | "internalType": "address", 114 | "name": "", 115 | "type": "address" 116 | } 117 | ], 118 | "payable": false, 119 | "stateMutability": "view", 120 | "type": "function" 121 | }, 122 | { 123 | "constant": true, 124 | "inputs": [], 125 | "name": "feeToSetter", 126 | "outputs": [ 127 | { 128 | "internalType": "address", 129 | "name": "", 130 | "type": "address" 131 | } 132 | ], 133 | "payable": false, 134 | "stateMutability": "view", 135 | "type": "function" 136 | }, 137 | { 138 | "constant": true, 139 | "inputs": [ 140 | { 141 | "internalType": "address", 142 | "name": "", 143 | "type": "address" 144 | }, 145 | { 146 | "internalType": "address", 147 | "name": "", 148 | "type": "address" 149 | } 150 | ], 151 | "name": "getPair", 152 | "outputs": [ 153 | { 154 | "internalType": "address", 155 | "name": "", 156 | "type": "address" 157 | } 158 | ], 159 | "payable": false, 160 | "stateMutability": "view", 161 | "type": "function" 162 | }, 163 | { 164 | "constant": false, 165 | "inputs": [ 166 | { 167 | "internalType": "address", 168 | "name": "_feeTo", 169 | "type": "address" 170 | } 171 | ], 172 | "name": "setFeeTo", 173 | "outputs": [], 174 | "payable": false, 175 | "stateMutability": "nonpayable", 176 | "type": "function" 177 | }, 178 | { 179 | "constant": false, 180 | "inputs": [ 181 | { 182 | "internalType": "address", 183 | "name": "_feeToSetter", 184 | "type": "address" 185 | } 186 | ], 187 | "name": "setFeeToSetter", 188 | "outputs": [], 189 | "payable": false, 190 | "stateMutability": "nonpayable", 191 | "type": "function" 192 | } 193 | ] -------------------------------------------------------------------------------- /test/CompoundBorrow.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | import { expect } from "chai"; 3 | import { Contract, Signer } from "ethers"; 4 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 5 | let owner: SignerWithAddress; 6 | let user1: SignerWithAddress; 7 | let user2: SignerWithAddress; 8 | let team: SignerWithAddress; 9 | 10 | let TokenPool1: Contract; 11 | let TokenPool2: Contract; 12 | let TokenPool3: Contract; 13 | let PoolFactory: Contract; 14 | let UniswapRouter: Contract; 15 | 16 | let RewardToken: Contract; 17 | let UNIToken: Contract; 18 | let WETH: Contract; 19 | let DAI: Contract; 20 | let compound: Contract; 21 | let uniswap: Contract; 22 | let externalStore: Contract; 23 | let yieldFarming: Contract; 24 | let Oracle1: Contract; 25 | let Oracle2: Contract; 26 | describe("YieldFarming", function () { 27 | beforeEach(async () => { 28 | [owner, user1, user2, team] = await ethers.getSigners(); 29 | 30 | RewardToken = await ethers.getContractAt( 31 | "IERC20", 32 | "0x514910771AF9Ca656af840dff83E8264EcF986CA" 33 | ); 34 | 35 | UNIToken = await ethers.getContractAt( 36 | "IERC20", 37 | "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984" 38 | ); 39 | 40 | DAI = await ethers.getContractAt( 41 | "IERC20", 42 | "0x6B175474E89094C44Da98b954EedeAC495271d0F" 43 | ); 44 | 45 | const UniswapRouterABI = require("./ABI/UniswapRouter.json"); 46 | UniswapRouter = await ethers.getContractAt( 47 | UniswapRouterABI, 48 | "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D" 49 | ); 50 | 51 | const uniswapFactoryAddress = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"; 52 | 53 | WETH = await ethers.getContractAt( 54 | "IERC20", 55 | "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" 56 | ); 57 | 58 | 59 | const currentTime = (await ethers.provider.getBlock("latest")).timestamp; 60 | 61 | await UniswapRouter.connect(team).swapETHForExactTokens( 62 | 10000000, 63 | [WETH.address, RewardToken.address], 64 | team.address, 65 | currentTime + 100000, 66 | { value: ethers.utils.parseEther("1000") } 67 | ); 68 | await UniswapRouter.connect(user1).swapETHForExactTokens( 69 | 10000000, 70 | [WETH.address, DAI.address], 71 | user1.address, 72 | currentTime + 100000, 73 | { value: ethers.utils.parseEther("1000") } 74 | ); 75 | await UniswapRouter.connect(user2).swapETHForExactTokens( 76 | 1000000, 77 | [WETH.address, DAI.address], 78 | user2.address, 79 | currentTime + 100000, 80 | { value: ethers.utils.parseEther("100") } 81 | ); 82 | 83 | await UniswapRouter.connect(user1).swapETHForExactTokens( 84 | 1000000, 85 | [WETH.address, UNIToken.address], 86 | user1.address, 87 | currentTime + 100000, 88 | { value: ethers.utils.parseEther("100") } 89 | ); 90 | await UniswapRouter.connect(user2).swapETHForExactTokens( 91 | 1000000, 92 | [WETH.address, UNIToken.address], 93 | user2.address, 94 | currentTime + 100000, 95 | { value: ethers.utils.parseEther("100") } 96 | ); 97 | const YieldFarming = await ethers.getContractFactory("YieldFarming"); 98 | const Compound = await ethers.getContractFactory("CompoundController"); 99 | const ExternalStore = await ethers.getContractFactory("ExternalStore"); 100 | 101 | compound = await Compound.deploy(); 102 | externalStore = await ExternalStore.deploy(""); 103 | 104 | yieldFarming = await YieldFarming.deploy(compound.address, UniswapRouter.address, externalStore.address); 105 | }); 106 | 107 | // const oracle1Addr = await TokenPool1.getOracleAddress(); 108 | // Oracle1 = await ethers.getContractAt("Oracle", oracle1Addr); 109 | // const oracle2Addr = await TokenPool2.getOracleAddress(); 110 | // Oracle2 = await ethers.getContractAt("Oracle", oracle2Addr); 111 | 112 | it("should deposit and withdraw successfully", async function () { 113 | console.log("1 " , user1.address); 114 | const UNITokenAmount =await UNIToken.connect(user1).balanceOf(user1.address); 115 | console.log(UNITokenAmount); 116 | const DAITokenAmount =await DAI.connect(user1).balanceOf(user1.address); 117 | console.log(DAITokenAmount); 118 | console.log("2 " , user2.address); 119 | 120 | const asset = "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"; // UNIToken 121 | const borrowAsset = "0xdAC17F958D2ee523a2206206994597C13D831ec7"; // USDT 122 | const path = ["0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"]; // DAI to WETH 123 | const amounts = [1000, 1000]; // Amounts for swapping 124 | 125 | // Deposit 126 | await UNIToken.connect(user1).approve(yieldFarming.address, 1000); 127 | await yieldFarming.connect(user1).deposit(asset, 1000, borrowAsset, 1000, path, amounts); 128 | }); 129 | // let yieldFarming: Contract; 130 | // let compound: Contract; 131 | // let uniswap: Contract; 132 | // let externalStore: Contract; 133 | 134 | // beforeEach(async function () { 135 | // const YieldFarming = await ethers.getContractFactory("YieldFarming"); 136 | // const Compound = await ethers.getContractFactory("CompoundController"); 137 | // const UniswapV2Router02 = await ethers.getContractFactory("IUniswapV2Router02"); 138 | // const ExternalStore = await ethers.getContractFactory("ExternalStore"); 139 | 140 | // compound = await Compound.deploy(); 141 | // uniswap = await UniswapV2Router02.deploy(); 142 | // externalStore = await ExternalStore.deploy(""); 143 | 144 | // yieldFarming = await YieldFarming.deploy(compound.address, uniswap.address, externalStore.address); 145 | // }); 146 | 147 | // it("should deposit and withdraw successfully", async function () { 148 | // // Mock token addresses 149 | // const asset = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; // DAI 150 | // const borrowAsset = "0xdAC17F958D2ee523a2206206994597C13D831ec7"; // USDT 151 | // const path = ["0x6B175474E89094C44Da98b954EedeAC495271d0F", "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"]; // DAI to WETH 152 | // const amounts = [1000, 1000]; // Amounts for swapping 153 | 154 | // // Deposit 155 | // await yieldFarming.deposit(asset, 1000, borrowAsset, 1000, path, amounts); 156 | 157 | // // Assert ERC1155 balances in external store 158 | // const assetBalance = await externalStore.balanceOf(yieldFarming.address, 1); 159 | // const borrowAssetBalance = await externalStore.balanceOf(yieldFarming.address, 2); 160 | // const liquidityBalance = await externalStore.balanceOf(yieldFarming.address, 11); 161 | // expect(assetBalance).to.equal(1000); 162 | // expect(borrowAssetBalance).to.equal(1000); 163 | // expect(liquidityBalance).to.be.above(0); 164 | 165 | // // Withdraw 166 | // await yieldFarming.withdraw(asset, 1000, borrowAsset, 1000, path, amounts); 167 | 168 | // // Assert ERC1155 balances in external store after withdrawal 169 | // const assetBalanceAfterWithdrawal = await externalStore.balanceOf(yieldFarming.address, 1); 170 | // const borrowAssetBalanceAfterWithdrawal = await externalStore.balanceOf(yieldFarming.address, 2); 171 | // const liquidityBalanceAfterWithdrawal = await externalStore.balanceOf(yieldFarming.address, 11); 172 | // expect(assetBalanceAfterWithdrawal).to.equal(0); 173 | // expect(borrowAssetBalanceAfterWithdrawal).to.equal(0); 174 | // expect(liquidityBalanceAfterWithdrawal).to.equal(0); 175 | // }); 176 | }); -------------------------------------------------------------------------------- /contracts/CompoundController.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:MIT 2 | pragma solidity ^0.8; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "./interface/compound.sol"; 6 | import "./interface/CompoundInterface.sol"; 7 | 8 | import "hardhat/console.sol"; 9 | 10 | //supply 11 | //redeem 12 | //borrow 13 | //repay 14 | 15 | contract CompoundController is ICompound{ 16 | //Borrow and Repay 17 | //Mainnet Comptroller address 18 | Comptroller public comptroller = 19 | Comptroller(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B); 20 | //chainlink contract mainnet address 21 | PriceFeed public priceFeed = 22 | PriceFeed(0x922018674c12a7F0D394ebEEf9B58F186CdE13c1); 23 | 24 | //Supply token into the Pool 25 | function supply(address token, address cToken, uint256 _amount) external { 26 | console.log("---supply---"); 27 | IERC20(token).transferFrom(msg.sender, address(this), _amount); 28 | IERC20(token).approve(address(cToken), _amount); 29 | console.log("ctoken Balance of this Address before mint: ", CErc20(cToken).balanceOf(address(this))); 30 | console.log("token Balance of this Address before mint",IERC20(token).balanceOf(address(this))); 31 | console.log("totalSupply of cToken before mint",CErc20(cToken).totalSupply()); 32 | console.log("token Balance of CToken contract before mint",IERC20(token).balanceOf(cToken)); 33 | //If a number is equals zero that means token is minted 34 | require(CErc20(cToken).mint(_amount) == 0, "mint failed"); 35 | console.log("-minted-"); 36 | console.log("ctoken Balance of this Address after mint: ", CErc20(cToken).balanceOf(address(this))); 37 | console.log("token Balance of this Address after mint",IERC20(token).balanceOf(address(this))); 38 | console.log("totalSupply of cToken after mint",CErc20(cToken).totalSupply()); 39 | console.log("token Balance of CToken contract after mint",IERC20(token).balanceOf(cToken)); 40 | // console.log("Price of cToken underlying",getPriceFeed(address(cToken))); 41 | } 42 | 43 | 44 | //Enter the market Borrow 45 | //Before borrowing first supply the token so that you can get ctoken to exchange 46 | function borrow(address cToken, address _cTokenToBorrow, uint256 borrowAmount) external { 47 | console.log("---borrow---"); 48 | //Enter the market before supply and borrow 49 | uint256 _decimals = 18; 50 | address[] memory cTokens = new address[](1); 51 | cTokens[0] = cToken; 52 | uint256[] memory _errors = comptroller.enterMarkets(cTokens); 53 | require(_errors[0] == 0, "enter market failed"); 54 | //check liquidity 55 | (uint256 _error, uint256 _liquidity, uint256 _shortfall) = comptroller.getAccountLiquidity(address(this)); 56 | require( 57 | _error == 0, 58 | "Sender either not authorized/Some internal factor is invalid" 59 | ); 60 | require(_shortfall == 0, "Borrowed over limit"); //The account is currently below the collateral requirement 61 | console.log("shortfall:", _shortfall, "liqudity:", _liquidity); 62 | require(_liquidity > 0, "Can't borrow"); 63 | 64 | //calculate Max borrow 65 | uint256 _priceFeed = priceFeed.getUnderlyingPrice(_cTokenToBorrow); 66 | // liquidity - USD scaled up by 1e18 67 | // price - USD scaled up by 1e18 68 | // decimals - decimals of token to borrow 69 | uint256 maxBorrow = (_liquidity * (10**_decimals))/_priceFeed ; 70 | require(maxBorrow > 0, "Maxborrow = 0"); 71 | // borrow 50% of max borrow 72 | require(maxBorrow >= borrowAmount, "Maxborrow < borrowAmount"); 73 | //Main Borrow Function 74 | require(CErc20(_cTokenToBorrow).borrow(borrowAmount) == 0, "Failed"); 75 | } 76 | 77 | function repayBorrow( 78 | address _tokenBorrowed, 79 | address _cTokenBorrowed, 80 | uint256 _amount 81 | ) external { 82 | IERC20(_tokenBorrowed).approve(_cTokenBorrowed, _amount); 83 | console.log(CErc20(_cTokenBorrowed).repayBorrow(_amount)); 84 | require( 85 | CErc20(_cTokenBorrowed).repayBorrow(_amount) == 0, 86 | "repay failed" 87 | ); 88 | } 89 | 90 | function getCTokenbalance(address cToken) external view returns (uint256) { 91 | //It will show the cToken balance of this account 92 | return CErc20(cToken).balanceOf(address(this)); 93 | } 94 | 95 | 96 | //Borrowed balance 97 | function getBorrowedBalance(address _cTokenBorrowed) 98 | external 99 | returns (uint256) 100 | { 101 | console.log( 102 | CErc20(_cTokenBorrowed).borrowBalanceCurrent(address(this)) 103 | ); 104 | return CErc20(_cTokenBorrowed).borrowBalanceCurrent(address(this)); 105 | } 106 | 107 | function getBorrowedRatePerBlock(address _cTokenBorrowed) 108 | external 109 | view 110 | returns (uint256) 111 | { 112 | return CErc20(_cTokenBorrowed).borrowRatePerBlock(); 113 | } 114 | 115 | //repay Borrowed Token 116 | 117 | 118 | //Note: Not a view functions 119 | function getInfo(address cToken) 120 | external 121 | returns (uint256 exchangeRate, uint256 supplyRate) 122 | { 123 | //Amount of current Exchange rate from cToken to underlying 124 | exchangeRate = CErc20(cToken).exchangeRateCurrent(); 125 | console.log("Exchange Rate:", exchangeRate); 126 | //Amount added to you supply balance this block / Interest Rate 127 | supplyRate = CErc20(cToken).supplyRatePerBlock(); 128 | console.log("Supply Rate:", supplyRate); 129 | } 130 | 131 | function estimateBalanceofUnderlying(address cToken) external returns (uint256) { 132 | uint256 ctokenBal = CErc20(cToken).balanceOf(address(this)); 133 | uint256 exchangeRate = CErc20(cToken).exchangeRateCurrent(); 134 | uint256 decimals = 18; //DAI = 18 decimals 135 | uint256 CtokenDecimal = 8; 136 | console.log( 137 | "Estimated Balance of Underlying:", 138 | (ctokenBal * exchangeRate) / 10**(18 + decimals - CtokenDecimal) 139 | ); 140 | return (ctokenBal * exchangeRate) / 10**(18 + decimals - CtokenDecimal); 141 | } 142 | 143 | //Direct method of getting balance 144 | function balanceUnderlying(address cToken) internal returns (uint256) { 145 | console.log( 146 | "Direct Function Balance of cToken:", 147 | CErc20(cToken).balanceOfUnderlying(address(this)) 148 | ); 149 | return CErc20(cToken).balanceOfUnderlying(address(this)); 150 | } 151 | 152 | //Redeem function to sell the cToken 153 | function redeem(address cToken, uint256 _amountCToken) external { 154 | require( CErc20(cToken).redeem(_amountCToken) == 0, "Process failed"); 155 | } 156 | 157 | 158 | 159 | //Collateral 160 | //It will return the percentage value of how much asset the user can borrow against the cToken they hold(in terms of USDC) 161 | function getCollateral(address cToken) external view returns (uint256) { 162 | (bool isListed, uint256 colFactor, bool isComped) = comptroller.markets(cToken); 163 | require(isListed, "Token is not listed"); 164 | require(isComped, "Not eligible"); //whether or not the suppliers and borrowers of an asset are eligible to receive "COMP" 165 | return colFactor; //divide this 1e18 to get value in percentage 166 | } 167 | 168 | //This function return, how much token user can borrow and it depends on the collateral factor 169 | // sum of (supplied balance of market entered * col factor) - borrowed 170 | function getAccountLiquidity() external view returns (uint256, uint256) { 171 | (uint256 _error, uint256 _liquidity, uint256 _shortfall) = comptroller 172 | .getAccountLiquidity(address(this)); 173 | require( 174 | _error == 0, 175 | "Sender either not authorized/Some internal factor is invalid" 176 | ); 177 | return (_liquidity, _shortfall); 178 | } 179 | 180 | //Get the price in USD with 6 decimal precision 181 | function getPriceFeed(address _cToken) internal view returns (uint256) { 182 | //scaled by 1e18 183 | return priceFeed.getUnderlyingPrice(_cToken); 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /contracts/YieldFarming.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; 6 | import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; 7 | import "@openzeppelin/contracts/access/Ownable.sol"; 8 | import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; 9 | import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol"; 10 | import "./interface/CompoundInterface.sol"; 11 | import "hardhat/console.sol"; 12 | 13 | 14 | 15 | interface IExternalStore { 16 | function wrap(address user, uint256 amount, uint256 id) external; 17 | function burn(address user, uint256 id, uint256 amount) external; 18 | function balanceOf(address user, uint256 id) external returns (uint256); 19 | } 20 | 21 | contract YieldFarming is ERC1155Holder, Ownable { 22 | ICompound public compound; 23 | IUniswapV2Router02 public uniswap; 24 | IExternalStore public externalStore; 25 | 26 | uint256 public constant DAI_WETH_POOL_ID = 1; 27 | 28 | constructor(address _compound, address _uniswap, address _externalStore) { 29 | compound = ICompound(_compound); 30 | uniswap = IUniswapV2Router02(_uniswap); 31 | externalStore = IExternalStore(_externalStore); 32 | } 33 | 34 | function deposit(address asset, uint256 assetAmount, address borrowAsset, uint256 borrowAmount, address[] memory path, uint256[] memory amounts) external { 35 | // Transfer asset from user to this contract 36 | IERC20(asset).transferFrom(msg.sender, address(this), assetAmount); 37 | 38 | // Supply asset to Compound 39 | IERC20(asset).approve(address(compound), assetAmount); 40 | compound.supply(asset, getCtokenAddress(asset), assetAmount); 41 | //console.log("balance",compound.getCTokenbalance(getCtokenAddress(asset))); 42 | 43 | // Borrow asset from Compound 44 | //IERC20(borrowAsset).approve(address(compound), borrowAmount); 45 | compound.borrow( getCtokenAddress(asset), getCtokenAddress(borrowAsset), borrowAmount); 46 | 47 | // Swap half of borrowed asset to WETH 48 | uint256[] memory swapAmounts = new uint256[](path.length); 49 | swapAmounts[0] = borrowAmount / 2; 50 | 51 | for (uint256 i = 1; i < path.length; i++) { 52 | swapAmounts[i] = getSwapAmount(path[i - 1], path[i], swapAmounts[i - 1]); 53 | } 54 | 55 | IERC20(path[0]).approve(address(uniswap), swapAmounts[0]); 56 | uniswap.swapExactTokensForTokens(swapAmounts[0], 0, path, address(this), block.timestamp + 1800); 57 | 58 | // Add liquidity to asset/WETH pool 59 | IERC20 weth = IERC20(getTokenAddress("WETH")); 60 | IERC20(path[path.length - 1]).approve(address(uniswap), amounts[amounts.length - 1]); 61 | (,, uint256 liquidity) = uniswap.addLiquidity(path[0], path[path.length - 1], swapAmounts[0], amounts[amounts.length - 1], 0, 0, address(this), block.timestamp + 1800); 62 | 63 | // Wrap deposited information to ERC1155 and store to ExternalStore contract 64 | externalStore.wrap(msg.sender, assetAmount, getTokenId(asset)); 65 | externalStore.wrap(msg.sender, borrowAmount, getTokenId(borrowAsset)); 66 | externalStore.wrap(msg.sender, liquidity, getTokenId(asset)+10); 67 | } 68 | 69 | function withdraw(address asset, uint256 assetAmount, address borrowAsset, uint256 borrowAmount, address[] memory path, uint256[] memory amounts) external { 70 | // Burn ERC1155 71 | uint256 liquidity = externalStore.balanceOf(msg.sender, getTokenId(asset)+10); 72 | externalStore.burn(msg.sender, getTokenId(asset), assetAmount); 73 | externalStore.burn(msg.sender, getTokenId(borrowAsset), borrowAmount); 74 | externalStore.burn(msg.sender, getTokenId(asset)+10, liquidity); 75 | 76 | // Remove liquidity from asset/WETH pool 77 | IERC20 weth = IERC20(getTokenAddress("WETH")); 78 | IERC20(path[path.length - 1]).approve(address(uniswap), amounts[amounts.length - 1]); 79 | (uint256 amountA, uint256 amountB) = uniswap.removeLiquidity(path[0], path[path.length - 1], liquidity, 0, 0, address(this), block.timestamp + 1800); 80 | 81 | // Swap WETH to asset 82 | uint256[] memory swapAmounts = new uint256[](path.length); 83 | swapAmounts[swapAmounts.length - 1] = amountB; 84 | 85 | for (uint256 i = swapAmounts.length - 2; i >= 0; i--) { 86 | swapAmounts[i] = getSwapAmount(path[i + 1], path[i], swapAmounts[i + 1]); 87 | } 88 | 89 | IERC20(path[path.length - 1]).approve(address(uniswap), swapAmounts[0]); 90 | uniswap.swapExactTokensForTokens(swapAmounts[0], 0, path, address(this), block.timestamp + 1800); 91 | 92 | // Repay asset debt to Compound 93 | IERC20(asset).approve(address(compound), borrowAmount); 94 | compound.repayBorrow(asset, borrowAsset, borrowAmount); 95 | 96 | // Redeem asset from Compound 97 | IERC20(asset).approve(address(compound), assetAmount); 98 | compound.repayBorrow(asset, borrowAsset, assetAmount); 99 | } 100 | function getTokenId(address token) private pure returns (uint256) { 101 | if (token == address(0x6B175474E89094C44Da98b954EedeAC495271d0F)) { //DAI 102 | return 1; 103 | } else if (token == address(0xdAC17F958D2ee523a2206206994597C13D831ec7)) { //USDT 104 | return 2; 105 | } else if (token == address(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984)) { //UNI 106 | return 3; 107 | } else { 108 | revert("Unsupported token address"); 109 | } 110 | } 111 | 112 | function getBorrowAmount(address asset, uint256 assetAmount, address borrowAsset) private view returns (uint256) { 113 | uint256 assetPrice = getAssetPrice(asset); 114 | uint256 borrowAssetPrice = getAssetPrice(borrowAsset); 115 | uint256 borrowAmount = (assetAmount * assetPrice) / borrowAssetPrice; 116 | return borrowAmount; 117 | } 118 | 119 | function getSwapAmount(address fromToken, address toToken, uint256 amountIn) private view returns (uint256) { 120 | address[] memory path = new address[](2); 121 | path[0] = fromToken; 122 | path[1] = toToken; 123 | uint256[] memory amountsOut = uniswap.getAmountsOut(amountIn, path); 124 | return amountsOut[amountsOut.length - 1]; 125 | } 126 | 127 | function getAssetPrice(address asset) private view returns (uint256) { 128 | (, int256 price, , , ) = AggregatorV3Interface(getPriceFeedAddress(asset)).latestRoundData(); 129 | return uint256(price); 130 | } 131 | function getTokenAddress(string memory symbol) private pure returns (address) { 132 | if (keccak256(bytes(symbol)) == keccak256(bytes("DAI"))) { 133 | return 0x6B175474E89094C44Da98b954EedeAC495271d0F; 134 | } else if (keccak256(bytes(symbol)) == keccak256(bytes("WETH"))) { 135 | return 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 136 | } else if (keccak256(bytes(symbol)) == keccak256(bytes("USDT"))) { 137 | return 0xdAC17F958D2ee523a2206206994597C13D831ec7; 138 | } else if (keccak256(bytes(symbol)) == keccak256(bytes("UNI"))) { 139 | return 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984; 140 | } else { 141 | revert("Unsupported token symbol"); 142 | } 143 | } 144 | function getPriceFeedAddress(address asset) private pure returns (address) { 145 | if (asset == address(0x6B175474E89094C44Da98b954EedeAC495271d0F)) { 146 | return 0x773616E4d11A78F511299002da57A0a94577F1f4; 147 | } else if (asset == address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)) { 148 | return 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419; 149 | } else if (asset == address(0xdAC17F958D2ee523a2206206994597C13D831ec7)) { 150 | return 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D; 151 | } else if (asset == address(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984)) { 152 | return 0x553303d460EE0afB37EdFf9bE42922D8FF63220e; 153 | } else { 154 | revert("Unsupported asset address"); 155 | } 156 | } 157 | function getCtokenAddress(address asset) private pure returns (address) { 158 | if (asset == address(0x6B175474E89094C44Da98b954EedeAC495271d0F)) { 159 | return 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643; 160 | } else if (asset == address(0xdAC17F958D2ee523a2206206994597C13D831ec7)) { 161 | return 0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9; 162 | } else if (asset == address(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984)) { 163 | return 0x35A18000230DA775CAc24873d00Ff85BccdeD550; 164 | } else { 165 | revert("Unsupported asset address"); 166 | } 167 | } 168 | } -------------------------------------------------------------------------------- /test/ABI/UniswapRouter.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_factory", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "address", 11 | "name": "_WETH", 12 | "type": "address" 13 | } 14 | ], 15 | "stateMutability": "nonpayable", 16 | "type": "constructor" 17 | }, 18 | { 19 | "inputs": [], 20 | "name": "WETH", 21 | "outputs": [ 22 | { 23 | "internalType": "address", 24 | "name": "", 25 | "type": "address" 26 | } 27 | ], 28 | "stateMutability": "view", 29 | "type": "function" 30 | }, 31 | { 32 | "inputs": [ 33 | { 34 | "internalType": "address", 35 | "name": "tokenA", 36 | "type": "address" 37 | }, 38 | { 39 | "internalType": "address", 40 | "name": "tokenB", 41 | "type": "address" 42 | }, 43 | { 44 | "internalType": "uint256", 45 | "name": "amountADesired", 46 | "type": "uint256" 47 | }, 48 | { 49 | "internalType": "uint256", 50 | "name": "amountBDesired", 51 | "type": "uint256" 52 | }, 53 | { 54 | "internalType": "uint256", 55 | "name": "amountAMin", 56 | "type": "uint256" 57 | }, 58 | { 59 | "internalType": "uint256", 60 | "name": "amountBMin", 61 | "type": "uint256" 62 | }, 63 | { 64 | "internalType": "address", 65 | "name": "to", 66 | "type": "address" 67 | }, 68 | { 69 | "internalType": "uint256", 70 | "name": "deadline", 71 | "type": "uint256" 72 | } 73 | ], 74 | "name": "addLiquidity", 75 | "outputs": [ 76 | { 77 | "internalType": "uint256", 78 | "name": "amountA", 79 | "type": "uint256" 80 | }, 81 | { 82 | "internalType": "uint256", 83 | "name": "amountB", 84 | "type": "uint256" 85 | }, 86 | { 87 | "internalType": "uint256", 88 | "name": "liquidity", 89 | "type": "uint256" 90 | } 91 | ], 92 | "stateMutability": "nonpayable", 93 | "type": "function" 94 | }, 95 | { 96 | "inputs": [ 97 | { 98 | "internalType": "address", 99 | "name": "token", 100 | "type": "address" 101 | }, 102 | { 103 | "internalType": "uint256", 104 | "name": "amountTokenDesired", 105 | "type": "uint256" 106 | }, 107 | { 108 | "internalType": "uint256", 109 | "name": "amountTokenMin", 110 | "type": "uint256" 111 | }, 112 | { 113 | "internalType": "uint256", 114 | "name": "amountETHMin", 115 | "type": "uint256" 116 | }, 117 | { 118 | "internalType": "address", 119 | "name": "to", 120 | "type": "address" 121 | }, 122 | { 123 | "internalType": "uint256", 124 | "name": "deadline", 125 | "type": "uint256" 126 | } 127 | ], 128 | "name": "addLiquidityETH", 129 | "outputs": [ 130 | { 131 | "internalType": "uint256", 132 | "name": "amountToken", 133 | "type": "uint256" 134 | }, 135 | { 136 | "internalType": "uint256", 137 | "name": "amountETH", 138 | "type": "uint256" 139 | }, 140 | { 141 | "internalType": "uint256", 142 | "name": "liquidity", 143 | "type": "uint256" 144 | } 145 | ], 146 | "stateMutability": "payable", 147 | "type": "function" 148 | }, 149 | { 150 | "inputs": [], 151 | "name": "factory", 152 | "outputs": [ 153 | { 154 | "internalType": "address", 155 | "name": "", 156 | "type": "address" 157 | } 158 | ], 159 | "stateMutability": "view", 160 | "type": "function" 161 | }, 162 | { 163 | "inputs": [ 164 | { 165 | "internalType": "uint256", 166 | "name": "amountOut", 167 | "type": "uint256" 168 | }, 169 | { 170 | "internalType": "uint256", 171 | "name": "reserveIn", 172 | "type": "uint256" 173 | }, 174 | { 175 | "internalType": "uint256", 176 | "name": "reserveOut", 177 | "type": "uint256" 178 | } 179 | ], 180 | "name": "getAmountIn", 181 | "outputs": [ 182 | { 183 | "internalType": "uint256", 184 | "name": "amountIn", 185 | "type": "uint256" 186 | } 187 | ], 188 | "stateMutability": "pure", 189 | "type": "function" 190 | }, 191 | { 192 | "inputs": [ 193 | { 194 | "internalType": "uint256", 195 | "name": "amountIn", 196 | "type": "uint256" 197 | }, 198 | { 199 | "internalType": "uint256", 200 | "name": "reserveIn", 201 | "type": "uint256" 202 | }, 203 | { 204 | "internalType": "uint256", 205 | "name": "reserveOut", 206 | "type": "uint256" 207 | } 208 | ], 209 | "name": "getAmountOut", 210 | "outputs": [ 211 | { 212 | "internalType": "uint256", 213 | "name": "amountOut", 214 | "type": "uint256" 215 | } 216 | ], 217 | "stateMutability": "pure", 218 | "type": "function" 219 | }, 220 | { 221 | "inputs": [ 222 | { 223 | "internalType": "uint256", 224 | "name": "amountOut", 225 | "type": "uint256" 226 | }, 227 | { 228 | "internalType": "address[]", 229 | "name": "path", 230 | "type": "address[]" 231 | } 232 | ], 233 | "name": "getAmountsIn", 234 | "outputs": [ 235 | { 236 | "internalType": "uint256[]", 237 | "name": "amounts", 238 | "type": "uint256[]" 239 | } 240 | ], 241 | "stateMutability": "view", 242 | "type": "function" 243 | }, 244 | { 245 | "inputs": [ 246 | { 247 | "internalType": "uint256", 248 | "name": "amountIn", 249 | "type": "uint256" 250 | }, 251 | { 252 | "internalType": "address[]", 253 | "name": "path", 254 | "type": "address[]" 255 | } 256 | ], 257 | "name": "getAmountsOut", 258 | "outputs": [ 259 | { 260 | "internalType": "uint256[]", 261 | "name": "amounts", 262 | "type": "uint256[]" 263 | } 264 | ], 265 | "stateMutability": "view", 266 | "type": "function" 267 | }, 268 | { 269 | "inputs": [ 270 | { 271 | "internalType": "uint256", 272 | "name": "amountA", 273 | "type": "uint256" 274 | }, 275 | { 276 | "internalType": "uint256", 277 | "name": "reserveA", 278 | "type": "uint256" 279 | }, 280 | { 281 | "internalType": "uint256", 282 | "name": "reserveB", 283 | "type": "uint256" 284 | } 285 | ], 286 | "name": "quote", 287 | "outputs": [ 288 | { 289 | "internalType": "uint256", 290 | "name": "amountB", 291 | "type": "uint256" 292 | } 293 | ], 294 | "stateMutability": "pure", 295 | "type": "function" 296 | }, 297 | { 298 | "inputs": [ 299 | { 300 | "internalType": "address", 301 | "name": "tokenA", 302 | "type": "address" 303 | }, 304 | { 305 | "internalType": "address", 306 | "name": "tokenB", 307 | "type": "address" 308 | }, 309 | { 310 | "internalType": "uint256", 311 | "name": "liquidity", 312 | "type": "uint256" 313 | }, 314 | { 315 | "internalType": "uint256", 316 | "name": "amountAMin", 317 | "type": "uint256" 318 | }, 319 | { 320 | "internalType": "uint256", 321 | "name": "amountBMin", 322 | "type": "uint256" 323 | }, 324 | { 325 | "internalType": "address", 326 | "name": "to", 327 | "type": "address" 328 | }, 329 | { 330 | "internalType": "uint256", 331 | "name": "deadline", 332 | "type": "uint256" 333 | } 334 | ], 335 | "name": "removeLiquidity", 336 | "outputs": [ 337 | { 338 | "internalType": "uint256", 339 | "name": "amountA", 340 | "type": "uint256" 341 | }, 342 | { 343 | "internalType": "uint256", 344 | "name": "amountB", 345 | "type": "uint256" 346 | } 347 | ], 348 | "stateMutability": "nonpayable", 349 | "type": "function" 350 | }, 351 | { 352 | "inputs": [ 353 | { 354 | "internalType": "address", 355 | "name": "token", 356 | "type": "address" 357 | }, 358 | { 359 | "internalType": "uint256", 360 | "name": "liquidity", 361 | "type": "uint256" 362 | }, 363 | { 364 | "internalType": "uint256", 365 | "name": "amountTokenMin", 366 | "type": "uint256" 367 | }, 368 | { 369 | "internalType": "uint256", 370 | "name": "amountETHMin", 371 | "type": "uint256" 372 | }, 373 | { 374 | "internalType": "address", 375 | "name": "to", 376 | "type": "address" 377 | }, 378 | { 379 | "internalType": "uint256", 380 | "name": "deadline", 381 | "type": "uint256" 382 | } 383 | ], 384 | "name": "removeLiquidityETH", 385 | "outputs": [ 386 | { 387 | "internalType": "uint256", 388 | "name": "amountToken", 389 | "type": "uint256" 390 | }, 391 | { 392 | "internalType": "uint256", 393 | "name": "amountETH", 394 | "type": "uint256" 395 | } 396 | ], 397 | "stateMutability": "nonpayable", 398 | "type": "function" 399 | }, 400 | { 401 | "inputs": [ 402 | { 403 | "internalType": "address", 404 | "name": "token", 405 | "type": "address" 406 | }, 407 | { 408 | "internalType": "uint256", 409 | "name": "liquidity", 410 | "type": "uint256" 411 | }, 412 | { 413 | "internalType": "uint256", 414 | "name": "amountTokenMin", 415 | "type": "uint256" 416 | }, 417 | { 418 | "internalType": "uint256", 419 | "name": "amountETHMin", 420 | "type": "uint256" 421 | }, 422 | { 423 | "internalType": "address", 424 | "name": "to", 425 | "type": "address" 426 | }, 427 | { 428 | "internalType": "uint256", 429 | "name": "deadline", 430 | "type": "uint256" 431 | } 432 | ], 433 | "name": "removeLiquidityETHSupportingFeeOnTransferTokens", 434 | "outputs": [ 435 | { 436 | "internalType": "uint256", 437 | "name": "amountETH", 438 | "type": "uint256" 439 | } 440 | ], 441 | "stateMutability": "nonpayable", 442 | "type": "function" 443 | }, 444 | { 445 | "inputs": [ 446 | { 447 | "internalType": "address", 448 | "name": "token", 449 | "type": "address" 450 | }, 451 | { 452 | "internalType": "uint256", 453 | "name": "liquidity", 454 | "type": "uint256" 455 | }, 456 | { 457 | "internalType": "uint256", 458 | "name": "amountTokenMin", 459 | "type": "uint256" 460 | }, 461 | { 462 | "internalType": "uint256", 463 | "name": "amountETHMin", 464 | "type": "uint256" 465 | }, 466 | { 467 | "internalType": "address", 468 | "name": "to", 469 | "type": "address" 470 | }, 471 | { 472 | "internalType": "uint256", 473 | "name": "deadline", 474 | "type": "uint256" 475 | }, 476 | { 477 | "internalType": "bool", 478 | "name": "approveMax", 479 | "type": "bool" 480 | }, 481 | { 482 | "internalType": "uint8", 483 | "name": "v", 484 | "type": "uint8" 485 | }, 486 | { 487 | "internalType": "bytes32", 488 | "name": "r", 489 | "type": "bytes32" 490 | }, 491 | { 492 | "internalType": "bytes32", 493 | "name": "s", 494 | "type": "bytes32" 495 | } 496 | ], 497 | "name": "removeLiquidityETHWithPermit", 498 | "outputs": [ 499 | { 500 | "internalType": "uint256", 501 | "name": "amountToken", 502 | "type": "uint256" 503 | }, 504 | { 505 | "internalType": "uint256", 506 | "name": "amountETH", 507 | "type": "uint256" 508 | } 509 | ], 510 | "stateMutability": "nonpayable", 511 | "type": "function" 512 | }, 513 | { 514 | "inputs": [ 515 | { 516 | "internalType": "address", 517 | "name": "token", 518 | "type": "address" 519 | }, 520 | { 521 | "internalType": "uint256", 522 | "name": "liquidity", 523 | "type": "uint256" 524 | }, 525 | { 526 | "internalType": "uint256", 527 | "name": "amountTokenMin", 528 | "type": "uint256" 529 | }, 530 | { 531 | "internalType": "uint256", 532 | "name": "amountETHMin", 533 | "type": "uint256" 534 | }, 535 | { 536 | "internalType": "address", 537 | "name": "to", 538 | "type": "address" 539 | }, 540 | { 541 | "internalType": "uint256", 542 | "name": "deadline", 543 | "type": "uint256" 544 | }, 545 | { 546 | "internalType": "bool", 547 | "name": "approveMax", 548 | "type": "bool" 549 | }, 550 | { 551 | "internalType": "uint8", 552 | "name": "v", 553 | "type": "uint8" 554 | }, 555 | { 556 | "internalType": "bytes32", 557 | "name": "r", 558 | "type": "bytes32" 559 | }, 560 | { 561 | "internalType": "bytes32", 562 | "name": "s", 563 | "type": "bytes32" 564 | } 565 | ], 566 | "name": "removeLiquidityETHWithPermitSupportingFeeOnTransferTokens", 567 | "outputs": [ 568 | { 569 | "internalType": "uint256", 570 | "name": "amountETH", 571 | "type": "uint256" 572 | } 573 | ], 574 | "stateMutability": "nonpayable", 575 | "type": "function" 576 | }, 577 | { 578 | "inputs": [ 579 | { 580 | "internalType": "address", 581 | "name": "tokenA", 582 | "type": "address" 583 | }, 584 | { 585 | "internalType": "address", 586 | "name": "tokenB", 587 | "type": "address" 588 | }, 589 | { 590 | "internalType": "uint256", 591 | "name": "liquidity", 592 | "type": "uint256" 593 | }, 594 | { 595 | "internalType": "uint256", 596 | "name": "amountAMin", 597 | "type": "uint256" 598 | }, 599 | { 600 | "internalType": "uint256", 601 | "name": "amountBMin", 602 | "type": "uint256" 603 | }, 604 | { 605 | "internalType": "address", 606 | "name": "to", 607 | "type": "address" 608 | }, 609 | { 610 | "internalType": "uint256", 611 | "name": "deadline", 612 | "type": "uint256" 613 | }, 614 | { 615 | "internalType": "bool", 616 | "name": "approveMax", 617 | "type": "bool" 618 | }, 619 | { 620 | "internalType": "uint8", 621 | "name": "v", 622 | "type": "uint8" 623 | }, 624 | { 625 | "internalType": "bytes32", 626 | "name": "r", 627 | "type": "bytes32" 628 | }, 629 | { 630 | "internalType": "bytes32", 631 | "name": "s", 632 | "type": "bytes32" 633 | } 634 | ], 635 | "name": "removeLiquidityWithPermit", 636 | "outputs": [ 637 | { 638 | "internalType": "uint256", 639 | "name": "amountA", 640 | "type": "uint256" 641 | }, 642 | { 643 | "internalType": "uint256", 644 | "name": "amountB", 645 | "type": "uint256" 646 | } 647 | ], 648 | "stateMutability": "nonpayable", 649 | "type": "function" 650 | }, 651 | { 652 | "inputs": [ 653 | { 654 | "internalType": "uint256", 655 | "name": "amountOut", 656 | "type": "uint256" 657 | }, 658 | { 659 | "internalType": "address[]", 660 | "name": "path", 661 | "type": "address[]" 662 | }, 663 | { 664 | "internalType": "address", 665 | "name": "to", 666 | "type": "address" 667 | }, 668 | { 669 | "internalType": "uint256", 670 | "name": "deadline", 671 | "type": "uint256" 672 | } 673 | ], 674 | "name": "swapETHForExactTokens", 675 | "outputs": [ 676 | { 677 | "internalType": "uint256[]", 678 | "name": "amounts", 679 | "type": "uint256[]" 680 | } 681 | ], 682 | "stateMutability": "payable", 683 | "type": "function" 684 | }, 685 | { 686 | "inputs": [ 687 | { 688 | "internalType": "uint256", 689 | "name": "amountOutMin", 690 | "type": "uint256" 691 | }, 692 | { 693 | "internalType": "address[]", 694 | "name": "path", 695 | "type": "address[]" 696 | }, 697 | { 698 | "internalType": "address", 699 | "name": "to", 700 | "type": "address" 701 | }, 702 | { 703 | "internalType": "uint256", 704 | "name": "deadline", 705 | "type": "uint256" 706 | } 707 | ], 708 | "name": "swapExactETHForTokens", 709 | "outputs": [ 710 | { 711 | "internalType": "uint256[]", 712 | "name": "amounts", 713 | "type": "uint256[]" 714 | } 715 | ], 716 | "stateMutability": "payable", 717 | "type": "function" 718 | }, 719 | { 720 | "inputs": [ 721 | { 722 | "internalType": "uint256", 723 | "name": "amountOutMin", 724 | "type": "uint256" 725 | }, 726 | { 727 | "internalType": "address[]", 728 | "name": "path", 729 | "type": "address[]" 730 | }, 731 | { 732 | "internalType": "address", 733 | "name": "to", 734 | "type": "address" 735 | }, 736 | { 737 | "internalType": "uint256", 738 | "name": "deadline", 739 | "type": "uint256" 740 | } 741 | ], 742 | "name": "swapExactETHForTokensSupportingFeeOnTransferTokens", 743 | "outputs": [], 744 | "stateMutability": "payable", 745 | "type": "function" 746 | }, 747 | { 748 | "inputs": [ 749 | { 750 | "internalType": "uint256", 751 | "name": "amountIn", 752 | "type": "uint256" 753 | }, 754 | { 755 | "internalType": "uint256", 756 | "name": "amountOutMin", 757 | "type": "uint256" 758 | }, 759 | { 760 | "internalType": "address[]", 761 | "name": "path", 762 | "type": "address[]" 763 | }, 764 | { 765 | "internalType": "address", 766 | "name": "to", 767 | "type": "address" 768 | }, 769 | { 770 | "internalType": "uint256", 771 | "name": "deadline", 772 | "type": "uint256" 773 | } 774 | ], 775 | "name": "swapExactTokensForETH", 776 | "outputs": [ 777 | { 778 | "internalType": "uint256[]", 779 | "name": "amounts", 780 | "type": "uint256[]" 781 | } 782 | ], 783 | "stateMutability": "nonpayable", 784 | "type": "function" 785 | }, 786 | { 787 | "inputs": [ 788 | { 789 | "internalType": "uint256", 790 | "name": "amountIn", 791 | "type": "uint256" 792 | }, 793 | { 794 | "internalType": "uint256", 795 | "name": "amountOutMin", 796 | "type": "uint256" 797 | }, 798 | { 799 | "internalType": "address[]", 800 | "name": "path", 801 | "type": "address[]" 802 | }, 803 | { 804 | "internalType": "address", 805 | "name": "to", 806 | "type": "address" 807 | }, 808 | { 809 | "internalType": "uint256", 810 | "name": "deadline", 811 | "type": "uint256" 812 | } 813 | ], 814 | "name": "swapExactTokensForETHSupportingFeeOnTransferTokens", 815 | "outputs": [], 816 | "stateMutability": "nonpayable", 817 | "type": "function" 818 | }, 819 | { 820 | "inputs": [ 821 | { 822 | "internalType": "uint256", 823 | "name": "amountIn", 824 | "type": "uint256" 825 | }, 826 | { 827 | "internalType": "uint256", 828 | "name": "amountOutMin", 829 | "type": "uint256" 830 | }, 831 | { 832 | "internalType": "address[]", 833 | "name": "path", 834 | "type": "address[]" 835 | }, 836 | { 837 | "internalType": "address", 838 | "name": "to", 839 | "type": "address" 840 | }, 841 | { 842 | "internalType": "uint256", 843 | "name": "deadline", 844 | "type": "uint256" 845 | } 846 | ], 847 | "name": "swapExactTokensForTokens", 848 | "outputs": [ 849 | { 850 | "internalType": "uint256[]", 851 | "name": "amounts", 852 | "type": "uint256[]" 853 | } 854 | ], 855 | "stateMutability": "nonpayable", 856 | "type": "function" 857 | }, 858 | { 859 | "inputs": [ 860 | { 861 | "internalType": "uint256", 862 | "name": "amountIn", 863 | "type": "uint256" 864 | }, 865 | { 866 | "internalType": "uint256", 867 | "name": "amountOutMin", 868 | "type": "uint256" 869 | }, 870 | { 871 | "internalType": "address[]", 872 | "name": "path", 873 | "type": "address[]" 874 | }, 875 | { 876 | "internalType": "address", 877 | "name": "to", 878 | "type": "address" 879 | }, 880 | { 881 | "internalType": "uint256", 882 | "name": "deadline", 883 | "type": "uint256" 884 | } 885 | ], 886 | "name": "swapExactTokensForTokensSupportingFeeOnTransferTokens", 887 | "outputs": [], 888 | "stateMutability": "nonpayable", 889 | "type": "function" 890 | }, 891 | { 892 | "inputs": [ 893 | { 894 | "internalType": "uint256", 895 | "name": "amountOut", 896 | "type": "uint256" 897 | }, 898 | { 899 | "internalType": "uint256", 900 | "name": "amountInMax", 901 | "type": "uint256" 902 | }, 903 | { 904 | "internalType": "address[]", 905 | "name": "path", 906 | "type": "address[]" 907 | }, 908 | { 909 | "internalType": "address", 910 | "name": "to", 911 | "type": "address" 912 | }, 913 | { 914 | "internalType": "uint256", 915 | "name": "deadline", 916 | "type": "uint256" 917 | } 918 | ], 919 | "name": "swapTokensForExactETH", 920 | "outputs": [ 921 | { 922 | "internalType": "uint256[]", 923 | "name": "amounts", 924 | "type": "uint256[]" 925 | } 926 | ], 927 | "stateMutability": "nonpayable", 928 | "type": "function" 929 | }, 930 | { 931 | "inputs": [ 932 | { 933 | "internalType": "uint256", 934 | "name": "amountOut", 935 | "type": "uint256" 936 | }, 937 | { 938 | "internalType": "uint256", 939 | "name": "amountInMax", 940 | "type": "uint256" 941 | }, 942 | { 943 | "internalType": "address[]", 944 | "name": "path", 945 | "type": "address[]" 946 | }, 947 | { 948 | "internalType": "address", 949 | "name": "to", 950 | "type": "address" 951 | }, 952 | { 953 | "internalType": "uint256", 954 | "name": "deadline", 955 | "type": "uint256" 956 | } 957 | ], 958 | "name": "swapTokensForExactTokens", 959 | "outputs": [ 960 | { 961 | "internalType": "uint256[]", 962 | "name": "amounts", 963 | "type": "uint256[]" 964 | } 965 | ], 966 | "stateMutability": "nonpayable", 967 | "type": "function" 968 | }, 969 | { 970 | "stateMutability": "payable", 971 | "type": "receive" 972 | } 973 | ] --------------------------------------------------------------------------------