diff --git a/contracts/multiply/DummyExchange.sol b/contracts/multiply/DummyExchange.sol index 65476c9..4e5ff87 100644 --- a/contracts/multiply/DummyExchange.sol +++ b/contracts/multiply/DummyExchange.sol @@ -90,6 +90,8 @@ contract DummyExchange { uint256 amountOut = (mul(amount, 10**18) / prices[asset]) / (10**(18 - precision)); _transferIn(msg.sender, DAI_ADDRESS, amount); emit AssetSwap(DAI_ADDRESS, asset, amount, amountOut); + console.log("DEBUG AMOUNT OUT", amountOut); + console.log("DEBUG msg.sender", msg.sender); _transferOut(asset, msg.sender, amountOut); } diff --git a/contracts/multiply/Exchange.sol b/contracts/multiply/Exchange.sol index bb7d3c1..a51afda 100644 --- a/contracts/multiply/Exchange.sol +++ b/contracts/multiply/Exchange.sol @@ -84,8 +84,10 @@ contract Exchange { ) internal returns (uint256) { IERC20(fromAsset).safeApprove(callee, amount); (bool success, ) = callee.call(withData); + console.log("DEBUG: swap status", success ? "succes" : "failure"); require(success, "Exchange / Could not swap"); uint256 balance = IERC20(toAsset).balanceOf(address(this)); + console.log("DEBUG: SWAPPED BALANCE", balance); emit SlippageSaved(receiveAtLeast, balance); require(balance >= receiveAtLeast, "Exchange / Received less"); emit AssetSwap(fromAsset, toAsset, amount, balance); @@ -93,7 +95,7 @@ contract Exchange { } function _collectFee(address asset, uint256 fromAmount) internal returns (uint256) { - uint256 feeToTransfer = (fromAmount.mul(fee)).div(feeBase); + uint256 feeToTransfer = fromAmount.sub((fromAmount.mul(feeBase)).div(feeBase.add(fee))); IERC20(asset).safeTransfer(feeBeneficiaryAddress, feeToTransfer); emit FeePaid(feeBeneficiaryAddress, feeToTransfer); return fromAmount.sub(feeToTransfer); @@ -115,8 +117,13 @@ contract Exchange { bytes calldata withData ) public { _transferIn(msg.sender, DAI_ADDRESS, amount); + console.log("DEBUG: AMOUNT", amount); + console.log("DEBUG: ASSET", asset); + console.log("DEBUG: receiveAtLEast", receiveAtLeast); + console.logBytes(withData); uint256 _amount = _collectFee(DAI_ADDRESS, amount); + console.log("DEBUG: AFTER FEE", _amount); uint256 balance = _swap(DAI_ADDRESS, asset, _amount, receiveAtLeast, callee, withData); uint256 daiBalance = IERC20(DAI_ADDRESS).balanceOf(address(this)); diff --git a/contracts/multiply/GuniMultiplyProxyActions.sol b/contracts/multiply/GuniMultiplyProxyActions.sol index 9c17598..0878336 100644 --- a/contracts/multiply/GuniMultiplyProxyActions.sol +++ b/contracts/multiply/GuniMultiplyProxyActions.sol @@ -17,6 +17,7 @@ import "../interfaces/misc/IGUNIToken.sol"; import "../interfaces/exchange/IExchange.sol"; import "./../flashMint/interface/IERC3156FlashBorrower.sol"; import "./../flashMint/interface/IERC3156FlashLender.sol"; +import "hardhat/console.sol"; struct CdpData { address gemJoin; @@ -75,7 +76,11 @@ contract GuniMultiplyProxyActions is IERC3156FlashBorrower { CdpData memory cdpData, GuniAddressRegistry calldata guniAddressRegistry ) public logMethodName("increaseMultipleGuni", cdpData, guniAddressRegistry.guniProxyActions) { - daiContract.transferFrom(msg.sender, guniAddressRegistry.guniProxyActions, cdpData.token0Amount); + daiContract.transferFrom( + msg.sender, + guniAddressRegistry.guniProxyActions, + cdpData.token0Amount + ); takeAFlashLoan(exchangeData, cdpData, guniAddressRegistry, 1); } @@ -102,7 +107,11 @@ contract GuniMultiplyProxyActions is IERC3156FlashBorrower { uint256 action ) internal { bytes memory paramsData = abi.encode(action, exchangeData, cdpData, guniAddressRegistry); - + console.log( + "DEBUG DAI BALANCE before FL GUNIPA", + IERC20(DAI).balanceOf(guniAddressRegistry.guniProxyActions) + ); + console.log("DEBUG DAI BALANCE before FL - DSPROXY", IERC20(DAI).balanceOf(address(this))); IManager(guniAddressRegistry.manager).cdpAllow( cdpData.cdpId, guniAddressRegistry.guniProxyActions, @@ -152,6 +161,9 @@ contract GuniMultiplyProxyActions is IERC3156FlashBorrower { uint256 bal1 = otherToken.balanceOf(address(this)); //120k bal0 = daiContract.balanceOf(address(this)); //80 k + console.log("DEBUG BAL0", bal0); + console.log("DEBUG BAL1", bal1); + { IGUNIRouter router = IGUNIRouter(guniAddressRegistry.router); daiContract.approve(address(router), bal0); @@ -160,12 +172,22 @@ contract GuniMultiplyProxyActions is IERC3156FlashBorrower { (, , guniBalance) = router.addLiquidity(address(guni), bal0, bal1, 0, 0, address(this)); } + console.log("DEBUG: GUNI BALANCE", guniBalance); + console.log("DEBUG: DAI BALANCE OF ", IERC20(DAI).balanceOf(address(this))); guni.approve(guniAddressRegistry.guniProxyActions, guniBalance); - joinDrawDebt(cdpData, borrowedDaiAmount, guniAddressRegistry.manager, guniAddressRegistry.jug); + joinDrawDebt( + cdpData, + borrowedDaiAmount.sub(IERC20(DAI).balanceOf(address(this))), + guniAddressRegistry.manager, + guniAddressRegistry.jug + ); uint256 daiLeft = IERC20(DAI).balanceOf(address(this)).sub(borrowedDaiAmount); uint256 otherTokenLeft = otherToken.balanceOf(address(this)); + console.log("DEBUG: DAI LEFTOVER", daiLeft); + console.log("DEBUG: USDC LEFTOVER", otherTokenLeft); + if (daiLeft > 0) { IERC20(DAI).transfer(cdpData.fundsReceiver, daiLeft); } @@ -236,6 +258,8 @@ contract GuniMultiplyProxyActions is IERC3156FlashBorrower { GuniAddressRegistry memory guniAddressRegistry ) = abi.decode(params, (uint256, ExchangeData, CdpData, GuniAddressRegistry)); + console.log("DEBUG: ON FLASH LOAN DAI BALANCE", IERC20(DAI).balanceOf(address(this))); + require(msg.sender == address(guniAddressRegistry.lender), "mpa-untrusted-lender"); uint256 borrowedDaiAmount; { diff --git a/contracts/multiply/MultiplyProxyActions.sol b/contracts/multiply/MultiplyProxyActions.sol index d45530d..97494c5 100644 --- a/contracts/multiply/MultiplyProxyActions.sol +++ b/contracts/multiply/MultiplyProxyActions.sol @@ -616,7 +616,7 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { uint256 daiLeft = IERC20(DAI).balanceOf(address(this)); require(cdpData.requiredDebt <= daiLeft, "cannot repay all debt"); - + wipeAndFreeGem( addressRegistry.manager, cdpData.gemJoin, diff --git a/tenderly.yaml b/tenderly.yaml index dfa85ab..8b60c63 100644 --- a/tenderly.yaml +++ b/tenderly.yaml @@ -12,7 +12,8 @@ exports: istanbul_block: 0 petersburg_block: 0 berlin_block: 0 + london_block: 0 forked_network: mainnet - project_slug: jinx/project + project_slug: georgi2/project protocol: "" rpc_address: 127.0.0.1:8545 \ No newline at end of file diff --git a/test/common/mcd-deployment-utils.js b/test/common/mcd-deployment-utils.js index 38b44dc..6a57de7 100644 --- a/test/common/mcd-deployment-utils.js +++ b/test/common/mcd-deployment-utils.js @@ -23,7 +23,7 @@ const { getVaultInfo } = require('../utils-mcd.js') const { curry } = require('ramda') const { getMarketPrice } = require('./http_apis') -const FEE = 2 +const FEE = 20 const FEE_BASE = 10000 const init = async function (blockNumber, provider, signer) { @@ -330,11 +330,11 @@ const deploySystem = async function (provider, signer, isExchangeDummy = false, deployedContracts.userProxyAddress = userProxyAddress deployedContracts.dsProxyInstance = dsProxy - // GUNI DEPLOYMENT + // GUNI DEPLOYMENT - const GUni = await ethers.getContractFactory('GuniMultiplyProxyActions', signer) - const guni = await GUni.deploy() - deployedContracts.guni = await guni.deployed() + const GUni = await ethers.getContractFactory('GuniMultiplyProxyActions', signer) + const guni = await GUni.deploy() + deployedContracts.guni = await guni.deployed() // const multiplyProxyActions = await deploy("MultiplyProxyActions"); const MPActions = await ethers.getContractFactory('MultiplyProxyActions', signer) diff --git a/test/exchange.js b/test/exchange.js index 450f551..6357470 100644 --- a/test/exchange.js +++ b/test/exchange.js @@ -97,11 +97,11 @@ describe('Exchange', async function () { await expect(tx).to.revertedWith('Exchange / Unauthorized Caller') }) - it.only('should allow beneficiary to update the fee', async function () { - const toTransferAmount = "0x"+amountToWei(1,18).toString(16); - let tx0 = await signer.populateTransaction({to:feeBeneficiary,value:toTransferAmount}); - await signer.sendTransaction(tx0); - await provider.send("hardhat_impersonateAccount", [feeBeneficiary]); + it('should allow beneficiary to update the fee', async function () { + const toTransferAmount = '0x' + amountToWei(1, 18).toString(16) + let tx0 = await signer.populateTransaction({ to: feeBeneficiary, value: toTransferAmount }) + await signer.sendTransaction(tx0) + await provider.send('hardhat_impersonateAccount', [feeBeneficiary]) const benef = await ethers.provider.getSigner(feeBeneficiary) let tx = await exchange.connect(benef).setFee('3') }) @@ -118,7 +118,7 @@ describe('Exchange', async function () { amountInWei, exchange.address, slippage.value.toString(), - ALLOWED_PROTOCOLS + ALLOWED_PROTOCOLS, ) initialDaiWalletBalance = convertToBigNumber(await balanceOf(MAINNET_ADRESSES.ETH, address)) @@ -139,19 +139,6 @@ describe('Exchange', async function () { await provider.send('evm_revert', [snapshotId]) }) - it('should not happen if it is triggered from unauthorized caller', async () => { - let tx = exchange - .connect(provider.getSigner(1)) - .swapTokenForDai( - MAINNET_ADRESSES.ETH, - amountToWei(1).toFixed(0), - amountFromWei(1).toFixed(0), - AGGREGATOR_V3_ADDRESS, - 0, - ) - await expect(tx).to.revertedWith('Exchange / Unauthorized Caller') - }) - describe('when transferring an exact amount to the exchange', async function () { let localSnapshotId, initialWethWalletBalance @@ -479,7 +466,7 @@ describe('Exchange', async function () { amountInWei.toFixed(0), slippage.value.toString(), exchange.address, - ALLOWED_PROTOCOLS + ALLOWED_PROTOCOLS, ) const { @@ -495,20 +482,6 @@ describe('Exchange', async function () { receiveAtLeastInWei = amountToWei(receiveAtLeast).toFixed(0) }) - it('should not happen if it is triggered from unauthorized caller', async () => { - let tx = exchange - .connect(provider.getSigner(1)) - .swapDaiForToken( - MAINNET_ADRESSES.ETH, - amountToWei(1).toFixed(0), - amountFromWei(1).toFixed(0), - AGGREGATOR_V3_ADDRESS, - 0, - ) - - await expect(tx).to.revertedWith('Exchange / Unauthorized Caller') - }) - describe('when transferring an exact amount to the exchange', async function () { let localSnapshotId @@ -956,7 +929,7 @@ describe('Exchange', async function () { amountInWei, exchange.address, slippage.value.toString(), - ALLOWED_PROTOCOLS + ALLOWED_PROTOCOLS, ) const { @@ -1070,7 +1043,7 @@ describe('Exchange', async function () { amountInWei.toFixed(0), slippage.value.toString(), exchange.address, - ALLOWED_PROTOCOLS + ALLOWED_PROTOCOLS, ) const { diff --git a/test/guni.js b/test/guni.js new file mode 100644 index 0000000..65414e0 --- /dev/null +++ b/test/guni.js @@ -0,0 +1,321 @@ +const { + deploySystem, + getOraclePrice, + dsproxyExecuteAction, + getLastCDP, + getVaultInfo, + balanceOf, + MAINNET_ADRESSES, + findMPAEvent, + swapTokens, +} = require('./common/mcd-deployment-utils') +const { default: BigNumber } = require('bignumber.js') +const { + amountToWei, + amountFromWei, + ensureWeiFormat, + calculateParamsIncreaseMP, + calculateParamsDecreaseMP, + prepareMultiplyParameters, + packMPAParams, + prepareMultiplyParameters2, +} = require('./common/params-calculation-utils') +const { expect } = require('chai') +const { one } = require('./utils') + +const wethAbi = require('../abi/IWETH.json') +const erc20Abi = require('../abi/IERC20.json') +const spotterAbi = require('../abi/ISpotter.json') +const GUNITokenAbi = require('../abi/IGUNIToken.json') + +const ethers = hre.ethers + +async function checkMPAPostState(tokenAddress, mpaAddress) { + const daiBalance = await balanceOf(MAINNET_ADRESSES.MCD_DAI, mpaAddress) + const collateralBalance = await balanceOf(tokenAddress, mpaAddress) + + return { + daiBalance: new BigNumber(daiBalance.toString()), + collateralBalance: new BigNumber(collateralBalance.toString()), + } +} + +(async function () { + + const addressRegistryFactory = function ( + multiplyProxyActionsInstanceAddress, + exchangeInstanceAddress, + ) { + return { + jug: '0x19c0976f590D67707E62397C87829d896Dc0f1F1', + manager: '0x5ef30b9986345249bc32d8928B7ee64DE9435E39', + multiplyProxyActions: multiplyProxyActionsInstanceAddress, + lender: '0x1EB4CF3A948E7D72A198fe073cCb8C7a948cD853', + feeRecepient: '0x79d7176aE8F93A04bC73b9BC710d4b44f9e362Ce', + exchange: exchangeInstanceAddress, + } + } + + + let provider, + signer, + address, + mcdView, + exchange, + multiplyProxyActions, + dsProxy, + userProxyAddress, + OF, + FF, + slippage, + exchangeDataMock, + DAI, + WETH, + guni + + let CDP_ID // this test suite operates on one Vault that is created in first test case (opening Multiply Vault) + let CDP_ILK + + provider = new hre.ethers.providers.JsonRpcProvider() + signer = provider.getSigner(0) + WETH = new ethers.Contract(MAINNET_ADRESSES.ETH, wethAbi, provider).connect(signer) + DAI = new ethers.Contract(MAINNET_ADRESSES.MCD_DAI, erc20Abi, provider).connect(signer) + address = await signer.getAddress() + + provider.send('hardhat_reset', [ + { + forking: { + jsonRpcUrl: process.env.ALCHEMY_NODE, + blockNumber: parseInt(process.env.BLOCK_NUMBER), + }, + }, + ]) + + const spotter = new ethers.Contract(MAINNET_ADRESSES.MCD_SPOT, spotterAbi, provider).connect(signer) + const USDC = new ethers.Contract(MAINNET_ADRESSES.USDC, erc20Abi, provider).connect(signer) + const GUNIDAIUSDCPool = new ethers.Contract("0xAbDDAfB225e10B90D798bB8A886238Fb835e2053", GUNITokenAbi, provider).connect(signer) + + let ilk = ethers.utils.formatBytes32String("GUNIV3DAIUSDC1-A") + const mat = await spotter.ilks(ilk); + + const deployment = await deploySystem(provider, signer, true) + + dsProxy = deployment.dsProxyInstance + multiplyProxyActions = deployment.multiplyProxyActionsInstance + mcdView = deployment.mcdViewInstance + userProxyAddress = deployment.userProxyAddress + exchange = deployment.exchangeInstance + guni = deployment.guni + + exchangeDataMock = { + to: exchange.address, + data: 0, + } + + const OazoFee = 20 // divided by base (10000), 1 = 0.01%; + OF = new BigNumber(OazoFee / 10000) // OAZO FEE + FF = new BigNumber(0.0009) // FLASHLOAN FEE + slippage = new BigNumber(0.0001) // Percent + + // await exchange.setSlippage(0); + // await exchange.setMode(0); + + await exchange.setFee(OazoFee) + + const receivedUSDC = amountToWei(new BigNumber(200), 6).toFixed(0) + const receivedDAI = amountToWei(new BigNumber(200), 18).toFixed(0) + + + await swapTokens( + MAINNET_ADRESSES.ETH, + MAINNET_ADRESSES.USDC, + amountToWei(new BigNumber(100), 18).toFixed(0), + receivedUSDC, + address, + provider, + signer, + ) + + await swapTokens( + MAINNET_ADRESSES.ETH, + MAINNET_ADRESSES.MCD_DAI, + amountToWei(new BigNumber(100), 18).toFixed(0), + receivedDAI, + address, + provider, + signer, + ) + + let balanceDAI = await balanceOf(MAINNET_ADRESSES.MCD_DAI, address); + let balanceUSDC = await balanceOf(MAINNET_ADRESSES.USDC, address); + // console.log('DAI balance', balanceDAI.toString()); + // console.log('USDC balance', balanceUSDC.toString()); + // console.log('DAI approval', balanceDAI.toString()); + + await DAI.approve(userProxyAddress, balanceDAI.toString()); + await USDC.approve(userProxyAddress, balanceUSDC.toString()); + USDC.transfer(exchange.address, balanceUSDC); + + const usdcDecimals = await USDC.decimals(); + + + let marketPrice, oraclePrice, currentColl, currentDebt, requiredCollRatio + + marketPrice = await new BigNumber(2380) + oraclePrice = await getOraclePrice(provider, MAINNET_ADRESSES.PIP_GUNIV3DAIUSDC1) + + console.log('ORACLE PRICE', oraclePrice.toString()); + + await exchange.setPrice(MAINNET_ADRESSES.ETH, amountToWei(marketPrice).toFixed(0)) + await exchange.setPrice(MAINNET_ADRESSES.USDC, amountToWei(new BigNumber(1)).toFixed(0)) + + currentColl = new BigNumber(100) // STARTING COLLATERAL AMOUNT + currentDebt = new BigNumber(0) // STARTING VAULT DEBT + + + requiredCollRatio = new BigNumber(3) + let [requiredDebt, toBorrowCollateralAmount] = calculateParamsIncreaseMP( + oraclePrice, + marketPrice, + OF, + FF, + currentColl, + currentDebt, + requiredCollRatio, + slippage, + ) + let desiredCdpState = { + requiredDebt, + toBorrowCollateralAmount, + providedCollateral: currentColl, + fromTokenAmount: requiredDebt, + toTokenAmount: toBorrowCollateralAmount, + } + + let { params, exchangeData, cdpData } = prepareMultiplyParameters( + exchangeDataMock, + desiredCdpState, + multiplyProxyActions.address, + exchange.address, + address, + false, + ) + + const guniDaiUsdc = '0xAbDDAfB225e10B90D798bB8A886238Fb835e2053'; + const gUniResolver = '0x0317650Af6f184344D7368AC8bB0bEbA5EDB214a' + // const amount = await guni.getOtherTokenAmount(guniDaiUsdc, gUniResolver, amountToWei(daiBal, 18).toFixed(0), 6); + + + const divider = amountFromWei(mat[1].toString(), 27).minus(1); + console.log('divider', divider.toString()); + + const daiBal = new BigNumber(10000); + const expectedCR = new BigNumber(1.05); + const leveragedAmount = daiBal.div(expectedCR.minus(one)); + const flAmount = leveragedAmount.minus(daiBal); + + let usdcAmount = await guni.getOtherTokenAmount(guniDaiUsdc, gUniResolver, amountToWei(leveragedAmount).toFixed(0), 6); + usdcAmount = new BigNumber(usdcAmount.toString()); + const daiAmount = leveragedAmount.times(10**18).minus(usdcAmount); + + console.log('USDC AMOUNT', usdcAmount.toString()); + console.log('DAI AMOUNT', daiAmount.toString()); + + const slippageFee = usdcAmount.times( new BigNumber(0.001).plus(OF)); + + const usdcAmountSlippageFee = usdcAmount.times( one.minus(0.001).minus(OF)); + console.log('usdcAmount', usdcAmountSlippageFee.toFixed(0)); + console.log('daiAmount', daiAmount.toFixed(0)); + + // const amounts = await GUNIDAIUSDCPool.getMintAmounts( + // amountToWei(daiAmount).toFixed(0), + // amountToWei(usdcAmountSlippageFee).toFixed(0) + // ) + + // console.log('AMOUNTS', amounts.mintAmount.toString() ); + + cdpData.gemJoin = "0xbFD445A97e7459b0eBb34cfbd3245750Dba4d7a4"; + cdpData.requiredDebt = amountToWei(flAmount).toFixed(0); + cdpData.token0Amount = amountToWei(daiBal).toFixed(0); + + exchangeData.fromTokenAmount = usdcAmount.toFixed(0); //amountToWei(daiBal).toFixed(0); // assuming 1 dai = 1 usdc . TO DO: change to DAI USDC swap with slippage !!! + exchangeData.fromTokenAddress = MAINNET_ADRESSES.MCD_DAI; + exchangeData.minToTokenAmount = usdcAmountSlippageFee.toFixed(0); + exchangeData.toTokenAddress = MAINNET_ADRESSES.USDC; + + const guniAddressRegistry = { + guni: '0xAbDDAfB225e10B90D798bB8A886238Fb835e2053', + resolver: '0x0317650Af6f184344D7368AC8bB0bEbA5EDB214a', + router: '0x14E6D67F824C3a7b4329d3228807f8654294e4bd', + guniProxyActions: guni.address, + otherToken: MAINNET_ADRESSES.USDC, + exchange: exchange.address, + jug: '0x19c0976f590D67707E62397C87829d896Dc0f1F1', + manager: '0x5ef30b9986345249bc32d8928B7ee64DE9435E39', + lender: '0x1EB4CF3A948E7D72A198fe073cCb8C7a948cD853', + } + + // MULTIPLY + + let params2 = [ + exchangeData, + cdpData, + guniAddressRegistry + ] + + var [status, result] = await dsproxyExecuteAction( + guni, + dsProxy, + address, + 'openMultiplyGuniVault', + params2, + 0 + ) + + if (status == false) { + throw result + } + + // // close to DAI + // cdpData.cdpId = 25897; + // cdpData.depositDai = 0; + + // let params4 = [ + // exchangeData, + // cdpData, + // guniAddressRegistry + // ] + + // var [status, result] = await dsproxyExecuteAction( + // guni, + // dsProxy, + // address, + // 'closeGuniVaultExitDai', + // params4, + // 0 + // ) + + const lastCDP = await getLastCDP(provider, signer, userProxyAddress) + console.log('lastCDP', lastCDP); + let info = await getVaultInfo(mcdView, lastCDP.id, lastCDP.ilk) + + console.log('CDP INFO', info); + + // CDP_ID = lastCDP.id + // CDP_ILK = lastCDP.ilk + + const currentCollRatio = new BigNumber(info.coll) + .times(oraclePrice) + .div(new BigNumber(info.debt)) + const { daiBalance, collateralBalance } = await checkMPAPostState( + MAINNET_ADRESSES.ETH, + multiplyProxyActions.address, + ) + + const requiredTotalCollateral = currentColl.plus(toBorrowCollateralAmount) + const resultTotalCollateral = new BigNumber(info.coll) + + console.log('RESULT COLLATERAL', resultTotalCollateral.toString()); + console.log('RESULT COLL RATIO', currentCollRatio.toString()); + +})() \ No newline at end of file