Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uniswap v3 #2

Merged
merged 2 commits into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading