### Goal This is the main Milkman interface: ``` function requestSwapExactTokensForTokens( uint256 amountIn, IERC20 fromToken, IERC20 toToken, address to, address priceChecker, bytes calldata priceCheckerData ) external; ``` It would be pretty cool if we could instead have it look like this: ``` function requestSwapExactTokensForTokens( uint256 amountIn, IERC20 fromToken, IERC20 toToken, address to ) external; ``` ### How The idea here would be to have an opinionated wrapper contract that sits on top of Milkman. When someone calls its main function, it would do something like this: 1. Determine if there are any supported paths for these tokens 2. `transferFrom` the user's `fromToken` into the wrapper contract 3. `fromToken.approve(amountIn)` Milkman 4. Call Milkman's `requestSwapExactTokensForTokens`, using a multi-way price checker for all supported paths Here's some dummy code for a wrapper that would only work for Curve or Chainlink ``` struct PriceFeed { bool exists; bool ethOrUsdToggle; // some feeds are eth based, some usd based } mapping(address => bool) feeds; function requestSwapExactTokensForTokens( uint256 amountIn, IERC20 fromToken, IERC20 toToken, address to ) external { bool shouldUseCurve = curveRegistry.find_pool_for_coins(fromToken, toToken) != address(0); bool shouldUseChainlink = feeds[fromToken].exists && feeds[toToken].exists; require(shouldUseCurve || shouldUseChainlink, "tokens not supported"); fromToken.transferFrom(msg.sender, address(this), amountIn); (address priceChecker, bytes priceCheckerData) = assemblePriceCheckerData(shouldUseCurve, shouldUseChainlink); fromToken.approve(amountIn, milkman); milkman.requestSwapExactTokensForTokens(amountIn, fromToken, toToken, to, priceChecker, priceCheckerData); } ``` You would likely need some maintenance work from the CoW team, for example to add new Uniswap V3 pools or to add new Chainlink feeds. Maybe we could set this up so that changes have to come through the CoW governance process.