diff --git a/contracts/interfaces/mcd/IChainLog.sol b/contracts/interfaces/mcd/IChainLog.sol new file mode 100644 index 0000000..0fbcb1a --- /dev/null +++ b/contracts/interfaces/mcd/IChainLog.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pragma solidity ^0.8.1; + +abstract contract IChainLog { + function getAddress(bytes32 _key) public view virtual returns (address addr); +} \ No newline at end of file diff --git a/contracts/interfaces/misc/IChainLogView.sol b/contracts/interfaces/misc/IChainLogView.sol new file mode 100644 index 0000000..b2c0b3d --- /dev/null +++ b/contracts/interfaces/misc/IChainLogView.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.1; + +abstract contract IChainLogView { + /** + * @notice Gets the address of a service by its name + * @param serviceName The name of the service + * @return The address of the service + */ + + function getServiceAddress(string calldata serviceName) external view virtual returns (address); + + /** + * @notice Gets the address of a join adapter by its ilk name + * @param ilkName The name of the ilk + * @return The address of the join adapter + */ + function getIlkJoinAddressByName(string calldata ilkName) external view virtual returns (address); + + /** + * @notice Gets the address of a join adapter by its ilk hash + * @param ilkHash The hash of the ilk name + * @return The address of the join adapter + */ + function getIlkJoinAddressByHash(bytes32 ilkHash) public view virtual returns (address); +} diff --git a/contracts/interfaces/misc/IProxy.sol b/contracts/interfaces/misc/IProxy.sol new file mode 100644 index 0000000..70b1935 --- /dev/null +++ b/contracts/interfaces/misc/IProxy.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.0; + +interface IProxy { + function owner() external view returns (address); +} diff --git a/contracts/multiply/MultiplyProxyActions.sol b/contracts/multiply/MultiplyProxyActions.sol index 8a505d4..6af43f7 100644 --- a/contracts/multiply/MultiplyProxyActions.sol +++ b/contracts/multiply/MultiplyProxyActions.sol @@ -26,10 +26,13 @@ import "../interfaces/mcd/IVat.sol"; import "../interfaces/mcd/IJug.sol"; import "../interfaces/mcd/IDaiJoin.sol"; import "../interfaces/exchange/IExchange.sol"; +import "../interfaces/misc/IProxy.sol"; +import "../interfaces/misc/IChainLogView.sol"; import "./ExchangeData.sol"; import "../flash-mint/interface/IERC3156FlashBorrower.sol"; import "../flash-mint/interface/IERC3156FlashLender.sol"; +import {console} from "hardhat/console.sol"; pragma solidity ^0.8.1; pragma abicoder v2; @@ -69,15 +72,26 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { address public immutable WETH; address public immutable DAI; address public immutable DAIJOIN; + address public immutable CDP_MANAGER; + address public immutable JUG; + address public immutable EXCHANGE; + address public immutable SELF; + address public immutable MakerChangeLog ; constructor( address _weth, address _dai, - address _daiJoin + address _daiJoin, + address _makerChangeLog ) { WETH = _weth; DAI = _dai; DAIJOIN = _daiJoin; + MakerChangeLog = _makerChangeLog; + SELF = address(this); + JUG = 0x19c0976f590D67707E62397C87829d896Dc0f1F1; + CDP_MANAGER = 0x5ef30b9986345249bc32d8928B7ee64DE9435E39; + EXCHANGE = 0xb5eB8cB6cED6b6f8E13bcD502fb489Db4a726C7B; } modifier logMethodName( @@ -92,6 +106,20 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { data.methodName = ""; } + function validateAndCorrectInputData(CdpData memory cdpData, AddressRegistry memory addressRegistry) public view{ + require(addressRegistry.jug == JUG, "mpa-jug-invalid"); + require(addressRegistry.manager == CDP_MANAGER, "mpa-manager-invalid"); + require(addressRegistry.multiplyProxyActions == SELF, "mpa-self-invalid"); + require(addressRegistry.lender == IChainLogView(MakerChangeLog).getServiceAddress("MCD_FLASH"), "mpa-FL-invalid"); + require(addressRegistry.exchange == EXCHANGE, "mpa-exchange-invalid"); + address cdpOwner = IProxy(IManager(CDP_MANAGER).owns(cdpData.cdpId)).owner(); + //address cdpOwner = IProxy(IManager(addressRegistry.manager).owns(cdpData.cdpId)).owner(); + bytes32 ilk = IJoin(cdpData.gemJoin).ilk(); + cdpData.ilk = ilk; + require(cdpData.fundsReceiver == cdpOwner, "mpa-fundsReceiver-not-owner"); + require(cdpData.gemJoin == IChainLogView(MakerChangeLog).getIlkJoinAddressByHash(cdpData.ilk), "mpa-wrong-gemJoin"); + } + function takeAFlashLoan( AddressRegistry memory addressRegistry, CdpData memory cdpData, @@ -102,7 +130,6 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { addressRegistry.multiplyProxyActions, 1 ); - IERC3156FlashLender(addressRegistry.lender).flashLoan( IERC3156FlashBorrower(addressRegistry.multiplyProxyActions), DAI, @@ -153,21 +180,23 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { function openMultiplyVault( ExchangeData calldata exchangeData, CdpData memory cdpData, - AddressRegistry calldata addressRegistry + AddressRegistry memory addressRegistry ) public payable logMethodName("openMultiplyVault", cdpData, addressRegistry.multiplyProxyActions) { + cdpData.ilk = IJoin(cdpData.gemJoin).ilk(); cdpData.cdpId = IManager(addressRegistry.manager).open(cdpData.ilk, address(this)); + validateAndCorrectInputData(cdpData, addressRegistry); increaseMultipleDepositCollateral(exchangeData, cdpData, addressRegistry); } function increaseMultipleDepositCollateral( ExchangeData calldata exchangeData, CdpData memory cdpData, - AddressRegistry calldata addressRegistry + AddressRegistry memory addressRegistry ) public payable @@ -177,6 +206,7 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { addressRegistry.multiplyProxyActions ) { + validateAndCorrectInputData(cdpData, addressRegistry); IGem gem = IJoin(cdpData.gemJoin).gem(); if (address(gem) == WETH) { @@ -204,7 +234,7 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { function drawDaiDebt( CdpData memory cdpData, - AddressRegistry calldata addressRegistry, + AddressRegistry memory addressRegistry, uint256 amount ) internal { address urn = IManager(addressRegistry.manager).urns(cdpData.cdpId); @@ -225,11 +255,12 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { function increaseMultipleDepositDai( ExchangeData calldata exchangeData, CdpData memory cdpData, - AddressRegistry calldata addressRegistry + AddressRegistry memory addressRegistry ) public logMethodName("increaseMultipleDepositDai", cdpData, addressRegistry.multiplyProxyActions) { + validateAndCorrectInputData(cdpData, addressRegistry); if (cdpData.skipFL) { IERC20(DAI).transferFrom(msg.sender, address(this), cdpData.depositDai); } else { @@ -245,15 +276,16 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { function increaseMultiple( ExchangeData calldata exchangeData, CdpData memory cdpData, - AddressRegistry calldata addressRegistry + AddressRegistry memory addressRegistry ) public logMethodName("increaseMultiple", cdpData, addressRegistry.multiplyProxyActions) { + validateAndCorrectInputData(cdpData, addressRegistry); increaseMultipleInternal(exchangeData, cdpData, addressRegistry); } function increaseMultipleInternal( ExchangeData calldata exchangeData, CdpData memory cdpData, - AddressRegistry calldata addressRegistry + AddressRegistry memory addressRegistry ) internal { cdpData.ilk = IJoin(cdpData.gemJoin).ilk(); @@ -280,15 +312,16 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { function decreaseMultiple( ExchangeData calldata exchangeData, CdpData memory cdpData, - AddressRegistry calldata addressRegistry + AddressRegistry memory addressRegistry ) public logMethodName("decreaseMultiple", cdpData, addressRegistry.multiplyProxyActions) { + validateAndCorrectInputData(cdpData, addressRegistry); decreaseMultipleInternal(exchangeData, cdpData, addressRegistry); } function decreaseMultipleInternal( ExchangeData calldata exchangeData, CdpData memory cdpData, - AddressRegistry calldata addressRegistry + AddressRegistry memory addressRegistry ) internal { cdpData.ilk = IJoin(cdpData.gemJoin).ilk(); @@ -304,33 +337,34 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { function decreaseMultipleWithdrawCollateral( ExchangeData calldata exchangeData, CdpData memory cdpData, - AddressRegistry calldata addressRegistry + AddressRegistry memory addressRegistry ) public logMethodName( "decreaseMultipleWithdrawCollateral", cdpData, - addressRegistry.multiplyProxyActions - ) + addressRegistry.multiplyProxyActions) { + validateAndCorrectInputData(cdpData, addressRegistry); decreaseMultipleInternal(exchangeData, cdpData, addressRegistry); } function decreaseMultipleWithdrawDai( ExchangeData calldata exchangeData, CdpData memory cdpData, - AddressRegistry calldata addressRegistry + AddressRegistry memory addressRegistry ) public logMethodName("decreaseMultipleWithdrawDai", cdpData, addressRegistry.multiplyProxyActions) { + validateAndCorrectInputData(cdpData, addressRegistry); decreaseMultipleInternal(exchangeData, cdpData, addressRegistry); } function closeVaultExitGeneric( ExchangeData calldata exchangeData, CdpData memory cdpData, - AddressRegistry calldata addressRegistry, + AddressRegistry memory addressRegistry, uint8 mode ) private { cdpData.ilk = IJoin(cdpData.gemJoin).ilk(); @@ -362,7 +396,7 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { function closeVaultExitCollateral( ExchangeData calldata exchangeData, CdpData memory cdpData, - AddressRegistry calldata addressRegistry + AddressRegistry memory addressRegistry ) public logMethodName("closeVaultExitCollateral", cdpData, addressRegistry.multiplyProxyActions) @@ -373,8 +407,9 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { function closeVaultExitDai( ExchangeData calldata exchangeData, CdpData memory cdpData, - AddressRegistry calldata addressRegistry + AddressRegistry memory addressRegistry ) public logMethodName("closeVaultExitDai", cdpData, addressRegistry.multiplyProxyActions) { + validateAndCorrectInputData(cdpData, addressRegistry); require(cdpData.skipFL == false, "cannot close to DAI if FL not used"); closeVaultExitGeneric(exchangeData, cdpData, addressRegistry, 3); } @@ -504,6 +539,9 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { IERC20(DAI).approve(address(exchange), exchangeData.fromTokenAmount.add(cdpData.depositDai)), "MPA / Could not approve Exchange for DAI" ); + console.log("exchange", address(exchange)); + console.log("before swap"); + console.logBytes(exchangeData._exchangeCalldata); exchange.swapDaiForToken( exchangeData.toTokenAddress, exchangeData.fromTokenAmount.add(cdpData.depositDai), @@ -511,6 +549,7 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { exchangeData.exchangeAddress, exchangeData._exchangeCalldata ); + console.log("after swap"); //here we add collateral we got from exchange, if skipFL then borrowedDai = 0 joinDrawDebt(cdpData, borrowedDai, addressRegistry.manager, addressRegistry.jug); //if some DAI are left after exchange return them to the user @@ -661,6 +700,7 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { uint256 borrowedDaiAmount, uint256 ink ) private { + // TODO: IExchange exchange = IExchange(addressRegistry.exchange); address gemAddress = address(IJoin(cdpData.gemJoin).gem()); @@ -769,7 +809,6 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { CdpData memory cdpData, AddressRegistry memory addressRegistry ) = abi.decode(params, (uint8, ExchangeData, CdpData, AddressRegistry)); - require(msg.sender == address(addressRegistry.lender), "mpa-untrusted-lender"); uint256 borrowedDaiAmount = amount.add(fee); @@ -804,7 +843,6 @@ contract MultiplyProxyActions is IERC3156FlashBorrower { cdpData.borrowCollateral ); } - IERC20(token).approve(addressRegistry.lender, borrowedDaiAmount); return keccak256("ERC3156FlashBorrower.onFlashLoan"); diff --git a/contracts/views/ChainLogView.sol b/contracts/views/ChainLogView.sol new file mode 100644 index 0000000..288a061 --- /dev/null +++ b/contracts/views/ChainLogView.sol @@ -0,0 +1,63 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.1; + +import { IChainLog } from "../interfaces/mcd/IChainLog.sol"; + +/** + * @title ChainLogView + * @notice Reads the Chainlog contract to get the address of a service by its name + */ +contract ChainLogView { + address public immutable chainlogAddress; + + constructor(address _chainlogAddress) { + chainlogAddress = _chainlogAddress; + } + + function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) { + uint8 i = 0; + while (i < 32 && _bytes32[i] != 0) { + i++; + } + bytes memory bytesArray = new bytes(i); + for (i = 0; i < 32 && _bytes32[i] != 0; i++) { + if (_bytes32[i] == bytes1("-")) { + bytesArray[i] = bytes1("_"); + } else { + bytesArray[i] = _bytes32[i]; + } + } + return string(bytesArray); + } + + /** + * @notice Gets the address of a service by its name + * @param serviceName The name of the service + * @return The address of the service + */ + + function getServiceAddress(string calldata serviceName) public view returns (address) { + bytes32 serviceHash = bytes32(abi.encodePacked(serviceName)); + return IChainLog(chainlogAddress).getAddress(serviceHash); + } + + /** + * @notice Gets the address of a join adapter by its ilk name + * @param ilkName The name of the ilk + * @return The address of the join adapter + */ + function getIlkJoinAddressByName(string calldata ilkName) public view returns (address) { + bytes32 ilkHash = bytes32(abi.encodePacked("MCD_JOIN_", ilkName)); + return IChainLog(chainlogAddress).getAddress(ilkHash); + } + + /** + * @notice Gets the address of a join adapter by its ilk hash + * @param ilkHash The hash of the ilk name + * @return The address of the join adapter + */ + function getIlkJoinAddressByHash(bytes32 ilkHash) public view returns (address) { + bytes32 newIlkHash = bytes32(abi.encodePacked("MCD_JOIN_", bytes32ToString(ilkHash))); + return IChainLog(chainlogAddress).getAddress(newIlkHash); + } +} diff --git a/hardhat.config.ts b/hardhat.config.ts index 1cdcd8e..45eb0bd 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -97,7 +97,7 @@ const config = { ), }, solidity: { - version: '0.8.1', + version: '0.8.14', settings: { optimizer: { enabled: true, diff --git a/test/common/cosntants.ts b/test/common/cosntants.ts index 4d8b8ad..5984ab9 100644 --- a/test/common/cosntants.ts +++ b/test/common/cosntants.ts @@ -12,4 +12,6 @@ export const ADDRESSES = { dai: '0x6b175474e89094c44da98b954eedeac495271d0f', weth: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', daijoin: '0x9759A6Ac90977b93B58547b4A71c78317f391A28', + chainlog: '0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F', + exchange: '0xb5eB8cB6cED6b6f8E13bcD502fb489Db4a726C7B', } diff --git a/test/common/utils/mcd-deployment.utils.ts b/test/common/utils/mcd-deployment.utils.ts index 40bdbdb..28edb5a 100644 --- a/test/common/utils/mcd-deployment.utils.ts +++ b/test/common/utils/mcd-deployment.utils.ts @@ -353,6 +353,7 @@ export interface DeployedSystemInfo { wethTokenInstance: Contract } guni: Contract + chainLogViewInstance: Contract } export async function deploySystem( @@ -378,8 +379,17 @@ export async function deploySystem( const guni = await GUni.deploy() deployedContracts.guni = await guni.deployed() + const chainLogViewFactory = await ethers.getContractFactory('ChainLogView', signer) + const chainLogView = await chainLogViewFactory.deploy(ADDRESSES.chainlog) + deployedContracts.chainLogViewInstance = await chainLogView.deployed() + const mpActionFactory = await ethers.getContractFactory('MultiplyProxyActions', signer) - const multiplyProxyActions = await mpActionFactory.deploy(ADDRESSES.weth, ADDRESSES.dai, ADDRESSES.daijoin); + const multiplyProxyActions = await mpActionFactory.deploy( + ADDRESSES.weth, + ADDRESSES.dai, + ADDRESSES.daijoin, + deployedContracts.chainLogViewInstance.address, + ) deployedContracts.multiplyProxyActionsInstance = await multiplyProxyActions.deployed() const mcdViewFactory = await ethers.getContractFactory('McdView', signer) @@ -391,7 +401,7 @@ export async function deploySystem( multiplyProxyActions.address, ADDRESSES.feeRecipient, FEE, - ADDRESSES.dai + ADDRESSES.dai, ) const exchangeInstance = await exchange.deployed() diff --git a/test/pa-open-multiply.test.ts b/test/pa-open-multiply.test.ts index cd9554f..da9f0c2 100644 --- a/test/pa-open-multiply.test.ts +++ b/test/pa-open-multiply.test.ts @@ -3,6 +3,7 @@ import { ethers } from 'hardhat' import { expect } from 'chai' import { JsonRpcProvider } from '@ethersproject/providers' import MAINNET_ADDRESSES from '../addresses/mainnet.json' +import { utils } from 'ethers' import { deploySystem, getOraclePrice, @@ -23,7 +24,7 @@ import { balanceOf, WETH_ADDRESS } from './utils' import { getVaultInfo } from './common/utils/mcd.utils' import { expectToBe, expectToBeEqual } from './common/utils/test.utils' import { CDPInfo, OneInchSwapResponse, VaultInfo } from './common/common.types' -import { one } from './common/cosntants' +import { ADDRESSES, one, ten } from './common/cosntants' interface FlattenedEvent { firstTopic: string @@ -50,6 +51,14 @@ function lookupEventByHash(events: FlattenedEvent[], eventHash: string) { return events.filter(x => x.firstTopic === eventHash) } +function forgeUnoswapCalldata(fromToken: string, fromAmount: string, toAmount: string, toDai = true): string { + const iface = new utils.Interface([ + 'function unoswap(address srcToken, uint256 amount, uint256 minReturn, bytes32[] calldata pools) public payable returns(uint256 returnAmount)', + ]) + const pool = `0x${toDai ? '8' : '0'}0000000000000003b6d0340a478c2975ab1ea89e8196811f51a7b7ade33eb11` + return iface.encodeFunctionData('unoswap', [fromToken, fromAmount, toAmount, [pool]]) +} + describe('Proxy Action', async () => { const baseCollateralAmountInETH = new BigNumber(10) const LENDER_FEE = new BigNumber(0) @@ -124,13 +133,22 @@ describe('Proxy Action', async () => { new BigNumber(data.desiredCollRatio), data.slippage, ) - + console.log("toBorrowCollateralAmount.toString()",toBorrowCollateralAmount.multipliedBy(ten.pow(18)).toFixed(0)) + const unoswapPayload = forgeUnoswapCalldata( + WETH_ADDRESS, + amountToWei(requiredDebt.times(one.minus(OAZO_FEE))).toFixed(0), + toBorrowCollateralAmount.multipliedBy(ten.pow(18)).toFixed(0), + false + ) const payload = await exchangeFromDAI( WETH_ADDRESS, amountToWei(requiredDebt.times(one.minus(OAZO_FEE))).toFixed(0), data.slippage.times(100).toFixed(), system.exchangeInstance.address, ) + + payload.tx.data = unoswapPayload; + data.oneInchPayload = payload.tx data.toBorrowCollateralAmount = toBorrowCollateralAmount data.desiredCDPState.requiredDebt = requiredDebt @@ -159,7 +177,7 @@ describe('Proxy Action', async () => { testCases[1].oneInchPayload, testCases[1].desiredCDPState, system.multiplyProxyActionsInstance.address, - system.exchangeInstance.address, + ADDRESSES.exchange, primarySignerAddress, false, 0, @@ -259,7 +277,7 @@ describe('Proxy Action', async () => { testCases[0].oneInchPayload, testCases[0].desiredCDPState, system.multiplyProxyActionsInstance.address, - system.exchangeInstance.address, + ADDRESSES.exchange, await primarySigner.getAddress(), false, 0,