Skip to content

Commit

Permalink
Uniswap v3 (#2)
Browse files Browse the repository at this point in the history
* ported uniswap v3 wrapper from erc3156

* Uniswap v3 tests pass

---------

Co-authored-by: alcueca <alberto@yield.is>
  • Loading branch information
alcueca and alcueca authored Jul 28, 2023
1 parent ca5cad7 commit 649e652
Show file tree
Hide file tree
Showing 12 changed files with 1,206 additions and 0 deletions.
138 changes: 138 additions & 0 deletions src/uniswapV3/ERC7399UniswapV3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Thanks to sunnyRK and yashnaman
pragma solidity ^0.8.0;

import { IUniswapV3FlashCallback } from "./interfaces/callback/IUniswapV3FlashCallback.sol";
import { IUniswapV3Pool } from "./interfaces/IUniswapV3Pool.sol";
import { IUniswapV3Factory } from "./interfaces/IUniswapV3Factory.sol";

import { TransferHelper } from "../utils/TransferHelper.sol";
import { FunctionCodec } from "../utils/FunctionCodec.sol";

import { IERC20 } from "lib/erc3156pp/src/interfaces/IERC20.sol";
import { IERC3156PPFlashLender } from "lib/erc3156pp/src/interfaces/IERC3156PPFlashLender.sol";

contract ERC7399UniswapV3 is IERC3156PPFlashLender, IUniswapV3FlashCallback {
using TransferHelper for IERC20;
using FunctionCodec for function(address, address, IERC20, uint256, uint256, bytes memory) external returns (bytes memory);
using FunctionCodec for bytes24;

// CONSTANTS
IUniswapV3Factory public factory;

// ACCESS CONTROL
IUniswapV3Pool internal _activePool;
bytes internal _callbackResult;

// DEFAULT ASSETS
IERC20 weth;
IERC20 dai;

/// @param factory_ Uniswap v3 UniswapV3Factory address
/// @param weth_ Weth contract used in Uniswap v3 Pairs
/// @param dai_ dai contract used in Uniswap v3 Pairs
constructor(
IUniswapV3Factory factory_,
IERC20 weth_,
IERC20 dai_
) {
factory = factory_;
weth = weth_;
dai = dai_;
}

/**
* @dev Get the Uniswap Pool that will be used as the source of a loan. The opposite asset will be Weth, except for Weth that will be Dai.
* @param asset The loan currency.
* @return The Uniswap V3 Pool that will be used as the source of the flash loan.
*/
function getPool(IERC20 asset) public view returns (IUniswapV3Pool) {
IERC20 assetOther = asset == weth ? dai : weth;
return IUniswapV3Pool(factory.getPool(address(asset), address(assetOther), 3000));
}

/**
* @dev From ERC-3156. The fee to be charged for a given loan.
* @param asset The loan currency.
* @param amount The amount of assets lent.
* @return The amount of `asset` to be charged for the loan, on top of the returned principal.
*/
function flashFee(IERC20 asset, uint256 amount) public view override returns (uint256) {
address pool = address(getPool(asset));
require(pool != address(0), "Unsupported currency");
if (asset.balanceOf(pool) <= amount) return type(uint256).max; // Not enough liquidity
uint256 wLoan = (amount * 1e6) / (1e6 - 3000); // 3000 = lpFees
uint256 wOwed = (wLoan * 1e6) / (1e6 - 3000); // 3000 = loanFees
uint256 fee = wOwed - wLoan;
return fee;
}

/// @dev Use the aggregator to serve an ERC3156++ flash loan.
/// @dev Forward the callback to the callback receiver. The borrower only needs to trust the aggregator and its governance, instead of the underlying lenders.
/// @param loanReceiver The address receiving the flash loan
/// @param asset The asset to be loaned
/// @param amount The amount to loaned
/// @param initiatorData The ABI encoded initiator data
/// @param callback The address and signature of the callback function
/// @return result ABI encoded result of the callback
function flashLoan(
address loanReceiver,
IERC20 asset,
uint256 amount,
bytes calldata initiatorData,
/// @dev callback.
/// This is a concatenation of (address, bytes4), where the address is the callback receiver, and the bytes4 is the signature of callback function.
/// The arguments in the callback function are fixed.
/// If the callback receiver needs to know the loan receiver, it should be encoded by the initiator in `data`.
/// @param initiator The address that called this function
/// @param paymentReceiver The address that needs to receive the amount plus fee at the end of the callback
/// @param asset The asset to be loaned
/// @param amount The amount to loaned
/// @param fee The fee to be paid
/// @param data The ABI encoded data to be passed to the callback
/// @return result ABI encoded result of the callback
function(address, address, IERC20, uint256, uint256, bytes memory) external returns (bytes memory) callback
) external returns (bytes memory) {
IUniswapV3Pool pool = getPool(asset);
require(address(pool) != address(0), "Unsupported currency");

IERC20 asset0 = IERC20(pool.token0());
IERC20 asset1 = IERC20(pool.token1());
uint amount0 = asset == asset0 ? amount : 0;
uint amount1 = asset == asset1 ? amount : 0;

bytes memory data = abi.encode(msg.sender, loanReceiver, asset, amount, callback.encodeFunction(), initiatorData);

_activePool = pool;
pool.flash(address(this), amount0, amount1, data);
delete _activePool;

bytes memory result = _callbackResult;
delete _callbackResult; // TODO: Confirm that this deletes the storage variable
return result;
}

// Flashswap Callback
function uniswapV3FlashCallback(
uint256, // Fee on Asset0
uint256, // Fee on Asset1
bytes calldata data
) external override {
require(msg.sender == address(_activePool), "Only active pool");

// decode data
(address initiator, address loanReceiver, IERC20 asset, uint256 amount, bytes24 encodedCallback, bytes memory initiatorData) = abi
.decode(data, (address, address, IERC20, uint256, bytes24, bytes));

function(address, address, IERC20, uint256, uint256, bytes memory) external returns (bytes memory) callback = encodedCallback.decodeFunction();

uint256 fee = flashFee(asset, amount);

// send the borrowed amount to the loan receiver
asset.safeTransfer(address(loanReceiver), amount);

// call the callback and tell the calback receiver to pay to the pool contract
// the callback result is kept in a storage variable to be retrieved later in this tx
_callbackResult = callback(initiator, msg.sender, asset, amount, fee, initiatorData);
}
}
78 changes: 78 additions & 0 deletions src/uniswapV3/interfaces/IUniswapV3Factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The interface for the Uniswap V3 Factory
/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees
interface IUniswapV3Factory {
/// @notice Emitted when the owner of the factory is changed
/// @param oldOwner The owner before the owner was changed
/// @param newOwner The owner after the owner was changed
event OwnerChanged(address indexed oldOwner, address indexed newOwner);

/// @notice Emitted when a pool is created
/// @param token0 The first token of the pool by address sort order
/// @param token1 The second token of the pool by address sort order
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @param tickSpacing The minimum number of ticks between initialized ticks
/// @param pool The address of the created pool
event PoolCreated(
address indexed token0,
address indexed token1,
uint24 indexed fee,
int24 tickSpacing,
address pool
);

/// @notice Emitted when a new fee amount is enabled for pool creation via the factory
/// @param fee The enabled fee, denominated in hundredths of a bip
/// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee
event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);

/// @notice Returns the current owner of the factory
/// @dev Can be changed by the current owner via setOwner
/// @return The address of the factory owner
function owner() external view returns (address);

/// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled
/// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context
/// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee
/// @return The tick spacing
function feeAmountTickSpacing(uint24 fee) external view returns (int24);

/// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
/// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
/// @param tokenA The contract address of either token0 or token1
/// @param tokenB The contract address of the other token
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @return pool The pool address
function getPool(
address tokenA,
address tokenB,
uint24 fee
) external view returns (address pool);

/// @notice Creates a pool for the given two tokens and fee
/// @param tokenA One of the two tokens in the desired pool
/// @param tokenB The other of the two tokens in the desired pool
/// @param fee The desired fee for the pool
/// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
/// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
/// are invalid.
/// @return pool The address of the newly created pool
function createPool(
address tokenA,
address tokenB,
uint24 fee
) external returns (address pool);

/// @notice Updates the owner of the factory
/// @dev Must be called by the current owner
/// @param _owner The new owner of the factory
function setOwner(address _owner) external;

/// @notice Enables a fee amount with the given tickSpacing
/// @dev Fee amounts may never be removed once enabled
/// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)
/// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount
function enableFeeAmount(uint24 fee, int24 tickSpacing) external;
}
24 changes: 24 additions & 0 deletions src/uniswapV3/interfaces/IUniswapV3Pool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import './pool/IUniswapV3PoolImmutables.sol';
import './pool/IUniswapV3PoolState.sol';
import './pool/IUniswapV3PoolDerivedState.sol';
import './pool/IUniswapV3PoolActions.sol';
import './pool/IUniswapV3PoolOwnerActions.sol';
import './pool/IUniswapV3PoolEvents.sol';

/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is
IUniswapV3PoolImmutables,
IUniswapV3PoolState,
IUniswapV3PoolDerivedState,
IUniswapV3PoolActions,
IUniswapV3PoolOwnerActions,
IUniswapV3PoolEvents
{

}
Loading

0 comments on commit 649e652

Please sign in to comment.