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

Use the Registry to initialize the uniswap wrapper #15

Merged
merged 7 commits into from
Aug 9, 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
cache
node_modules
out
.idea

# files
*.env
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@
[submodule "lib/erc7399"]
path = lib/erc7399
url = https://github.com/alcueca/erc7399
[submodule "lib/registry"]
path = lib/registry
url = https://github.com/alcueca/registry
1 change: 1 addition & 0 deletions lib/registry
Submodule registry added at 8e9808
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"scripts": {
"clean": "rm -rf cache out",
"lint": "pnpm lint:sol && pnpm prettier:check",
"lint:sol": "forge fmt --check && pnpm solhint {script,src,test}/**/*.sol",
"lint:sol": "forge fmt --check && pnpm solhint src/**/*.sol",
"prettier:check": "prettier --check **/*.{json,md,yml} --ignore-path=.prettierignore",
"prettier:write": "prettier --write **/*.{json,md,yml} --ignore-path=.prettierignore"
}
Expand Down
38 changes: 37 additions & 1 deletion script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,44 @@
pragma solidity >=0.8.19 <=0.9.0;

import { BaseScript } from "./Base.s.sol";
import { console2 } from "forge-std/console2.sol";

import { Registry } from "lib/registry/src/Registry.sol";

import { UniswapV3Wrapper } from "../src/uniswapV3/UniswapV3Wrapper.sol";
import { AaveWrapper, IPoolAddressesProvider } from "../src/aave/AaveWrapper.sol";
import { BalancerWrapper, IFlashLoaner } from "../src/balancer/BalancerWrapper.sol";

/// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting
contract Deploy is BaseScript {
function run() public broadcast { }
bytes32 public constant SALT = keccak256("ERC7399-wrappers");

address internal factory = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
address internal usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address internal usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
address internal weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address internal wbtc = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;

IFlashLoaner internal balancer = IFlashLoaner(0xBA12222222228d8Ba445958a75a0704d566BF2C8);

IPoolAddressesProvider internal provider = IPoolAddressesProvider(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb);

function run() public broadcast {
require(block.chainid == 42_161, "Only deploy on Arbitrum");

Registry registry = new Registry{salt: SALT}(broadcaster);

console2.log("Registry deployed at: %s", address(registry));

registry.set("UniswapV3Wrapper", abi.encode(factory, weth, usdc, usdt));

UniswapV3Wrapper uniswapV3Wrapper = new UniswapV3Wrapper{salt: SALT}(registry);
console2.log("UniswapV3Wrapper deployed at: %s", address(uniswapV3Wrapper));

BalancerWrapper balancerWrapper = new BalancerWrapper{salt: SALT}(balancer);
console2.log("BalancerWrapper deployed at: %s", address(balancerWrapper));

AaveWrapper aaveWrapper = new AaveWrapper{salt: SALT}(provider);
console2.log("AaveWrapper deployed at: %s", address(aaveWrapper));
}
}
2 changes: 1 addition & 1 deletion src/BaseWrapper.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
// Thanks to ultrasecr.eth
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

import "erc7399/IERC7399.sol";

Expand Down
11 changes: 8 additions & 3 deletions src/aave/AaveWrapper.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
// Thanks to ultrasecr.eth
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

import { IPool } from "./interfaces/IPool.sol";
import { DataTypes } from "./interfaces/DataTypes.sol";
Expand All @@ -18,7 +18,12 @@ contract AaveWrapper is BaseWrapper, IFlashLoanSimpleReceiver {
using FixedPointMathLib for uint256;
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;

error NotPool();
error NotInitiator();

// solhint-disable-next-line var-name-mixedcase
IPoolAddressesProvider public immutable ADDRESSES_PROVIDER;
// solhint-disable-next-line var-name-mixedcase
IPool public immutable POOL;

constructor(IPoolAddressesProvider provider) {
Expand Down Expand Up @@ -50,8 +55,8 @@ contract AaveWrapper is BaseWrapper, IFlashLoanSimpleReceiver {
override
returns (bool)
{
require(msg.sender == address(POOL), "AaveFlashLoanProvider: not pool");
require(initiator == address(this), "AaveFlashLoanProvider: not initiator");
if (msg.sender != address(POOL)) revert NotPool();
if (initiator != address(this)) revert NotInitiator();

_bridgeToCallback(asset, amount, fee, params);

Expand Down
2 changes: 1 addition & 1 deletion src/aave/interfaces/DataTypes.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

library DataTypes {
struct ReserveData {
Expand Down
2 changes: 1 addition & 1 deletion src/aave/interfaces/Errors.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

/**
* @title Errors library
Expand Down
2 changes: 1 addition & 1 deletion src/aave/interfaces/IFlashLoanSimpleReceiver.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

import { IPoolAddressesProvider } from "./IPoolAddressesProvider.sol";
import { IPool } from "./IPool.sol";
Expand Down
2 changes: 1 addition & 1 deletion src/aave/interfaces/IPool.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

import { IPoolAddressesProvider } from "./IPoolAddressesProvider.sol";
import { DataTypes } from "./DataTypes.sol";
Expand Down
2 changes: 1 addition & 1 deletion src/aave/interfaces/IPoolAddressesProvider.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

/**
* @title IPoolAddressesProvider
Expand Down
2 changes: 1 addition & 1 deletion src/aave/interfaces/ReserveConfiguration.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

import { Errors } from "./Errors.sol";
import { DataTypes } from "./DataTypes.sol";
Expand Down
9 changes: 6 additions & 3 deletions src/balancer/BalancerWrapper.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
// Thanks to ultrasecr.eth
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

import { IFlashLoanRecipient } from "./interfaces/IFlashLoanRecipient.sol";
import { IFlashLoaner } from "./interfaces/IFlashLoaner.sol";
Expand All @@ -18,6 +18,9 @@ contract BalancerWrapper is BaseWrapper, IFlashLoanRecipient {
using Arrays for address;
using FixedPointMathLib for uint256;

error NotBalancer();
error HashMismatch();

IFlashLoaner public immutable balancer;

bytes32 private flashLoanDataHash;
Expand Down Expand Up @@ -48,8 +51,8 @@ contract BalancerWrapper is BaseWrapper, IFlashLoanRecipient {
external
override
{
require(msg.sender == address(balancer), "BalancerWrapper: not balancer");
require(keccak256(params) == flashLoanDataHash, "BalancerWrapper: params hash mismatch");
if (msg.sender != address(balancer)) revert NotBalancer();
if (keccak256(params) != flashLoanDataHash) revert HashMismatch();
delete flashLoanDataHash;

_bridgeToCallback(assets[0], amounts[0], fees[0], params);
Expand Down
2 changes: 1 addition & 1 deletion src/balancer/interfaces/IFlashLoanRecipient.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

interface IFlashLoanRecipient {
/**
Expand Down
2 changes: 1 addition & 1 deletion src/balancer/interfaces/IFlashLoaner.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

import { IFlashLoanRecipient } from "./IFlashLoanRecipient.sol";
import { IProtocolFeesCollector } from "./IProtocolFeesCollector.sol";
Expand Down
2 changes: 1 addition & 1 deletion src/balancer/interfaces/IProtocolFeesCollector.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

interface IProtocolFeesCollector {
function getFlashLoanFeePercentage() external view returns (uint256);
Expand Down
4 changes: 2 additions & 2 deletions src/erc3156/ERC3156Wrapper.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

import { IERC3156FlashLender } from "lib/erc3156/contracts/interfaces/IERC3156FlashLender.sol";
import { IERC3156FlashBorrower } from "lib/erc3156/contracts/interfaces/IERC3156FlashBorrower.sol";
Expand All @@ -14,7 +14,7 @@ import { BaseWrapper, IERC7399 } from "../BaseWrapper.sol";
contract ERC3156Wrapper is BaseWrapper, IERC3156FlashBorrower {
bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");

mapping(address => IERC3156FlashLender) public lenders;
mapping(address asset => IERC3156FlashLender lender) public lenders;

/**
* @param assets_ Asset contracts supported for flash lending.
Expand Down
47 changes: 31 additions & 16 deletions src/uniswapV3/UniswapV3Wrapper.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
// Thanks to sunnyRK and yashnaman
pragma solidity ^0.8.0;
// Thanks to sunnyRK, yashnaman & ultrasecr.eth
pragma solidity ^0.8.19;

import { Registry } from "lib/registry/src/Registry.sol";

import { IUniswapV3FlashCallback } from "./interfaces/callback/IUniswapV3FlashCallback.sol";
import { IUniswapV3Pool } from "./interfaces/IUniswapV3Pool.sol";
Expand All @@ -14,23 +16,24 @@ contract UniswapV3Wrapper is BaseWrapper, IUniswapV3FlashCallback {
using PoolAddress for address;
using { canLoan, balance } for IUniswapV3Pool;

error UnknownPool();
error UnsupportedCurrency(address asset);

// CONSTANTS
address public immutable factory;

// DEFAULT ASSETS
address weth;
address usdc;
address usdt;

/// @param factory_ Uniswap v3 UniswapV3Factory address
/// @param weth_ Weth contract used in Uniswap v3 Pairs
/// @param usdc_ usdc contract used in Uniswap v3 Pairs
/// @param usdt_ usdt contract used in Uniswap v3 Pairs
constructor(address factory_, address weth_, address usdc_, address usdt_) {
factory = factory_;
weth = weth_;
usdc = usdc_;
usdt = usdt_;
address public immutable weth;
address public immutable usdc;
address public immutable usdt;

/// @param reg Registry storing constructor parameters
constructor(Registry reg) {
// @param factory_ Uniswap v3 UniswapV3Factory address
// @param weth_ Weth contract used in Uniswap v3 Pairs
// @param usdc_ usdc contract used in Uniswap v3 Pairs
// @param usdt_ usdt contract used in Uniswap v3 Pairs
(factory, weth, usdc, usdt) = abi.decode(reg.get("UniswapV3Wrapper"), (address, address, address, address));
}

/**
Expand Down Expand Up @@ -68,6 +71,18 @@ contract UniswapV3Wrapper is BaseWrapper, IUniswapV3FlashCallback {
return amount >= max ? type(uint256).max : _flashFee(asset, amount);
}

function _flashLoan(address asset, uint256 amount, bytes memory data) internal override {
IUniswapV3Pool pool = cheapestPool(asset, amount);
if (address(pool) == address(0)) revert UnsupportedCurrency(asset);

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

pool.flash(address(this), amount0, amount1, abi.encode(asset0, asset1, pool.fee(), amount, data));
}

/// @inheritdoc IUniswapV3FlashCallback
function uniswapV3FlashCallback(
uint256 fee0, // Fee on Asset0
Expand All @@ -79,7 +94,7 @@ contract UniswapV3Wrapper is BaseWrapper, IUniswapV3FlashCallback {
{
(address asset, address other, uint24 feeTier, uint256 amount, bytes memory data) =
abi.decode(params, (address, address, uint24, uint256, bytes));
require(msg.sender == address(_pool(asset, other, feeTier)), "UniswapV3Wrapper: Unknown pool");
if (msg.sender != address(_pool(asset, other, feeTier))) revert UnknownPool();

uint256 fee = fee0 > 0 ? fee0 : fee1;
_bridgeToCallback(asset, amount, fee, data);
Expand Down
2 changes: 1 addition & 1 deletion src/utils/Arrays.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Thanks to ultrasecr.eth
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

library Arrays {
function toArray(uint256 n) internal pure returns (uint256[] memory arr) {
Expand Down
3 changes: 2 additions & 1 deletion src/utils/RevertMsgExtractor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Taken from
// https://github.com/sushiswap/BoringSolidity/blob/441e51c0544cf2451e6116fe00515e71d7c42e2c/src/BoringBatchable.sol

pragma solidity >=0.6.0;
pragma solidity ^0.8.19;

library RevertMsgExtractor {
/// @dev Helper function to extract a useful revert message from a failed call.
Expand All @@ -11,6 +11,7 @@ library RevertMsgExtractor {
// If the _res length is less than 68, then the transaction failed silently (without a revert message)
if (returnData.length < 68) return "Transaction reverted silently";

// solhint-disable-next-line no-inline-assembly
assembly {
// Slice the sighash.
returnData := add(returnData, 0x04)
Expand Down
7 changes: 4 additions & 3 deletions src/utils/TransferHelper.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: MIT
// Taken from https://github.com/Uniswap/uniswap-lib/blob/master/src/libraries/TransferHelper.sol
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

import { ERC20 } from "solmate/tokens/ERC20.sol";
import "./RevertMsgExtractor.sol";
import { RevertMsgExtractor } from "./RevertMsgExtractor.sol";

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
// USDT is a well known token that returns nothing for its transfer, transferFrom, and approve functions
Expand All @@ -16,7 +16,8 @@ library TransferHelper {
/// @param value The value of the transfer
function safeTransfer(address asset, address to, uint256 value) internal {
(bool success, bytes memory data) =
address(asset).call(abi.encodeWithSelector(ERC20.transfer.selector, to, value));
// solhint-disable-next-line avoid-low-level-calls
address(asset).call(abi.encodeWithSelector(ERC20.transfer.selector, to, value));
if (!(success && (data.length == 0 || abi.decode(data, (bool))))) revert(RevertMsgExtractor.getRevertMsg(data));
}
}
4 changes: 2 additions & 2 deletions test/AaveWrapper.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ contract AaveWrapperTest is PRBTest, StdCheats {
}

function test_executeOperation_permissions() public {
vm.expectRevert("AaveFlashLoanProvider: not pool");
vm.expectRevert(AaveWrapper.NotPool.selector);
wrapper.executeOperation({ asset: address(dai), amount: 1e18, fee: 0, initiator: address(wrapper), params: "" });

vm.prank(provider.getPool());
vm.expectRevert("AaveFlashLoanProvider: not initiator");
vm.expectRevert(AaveWrapper.NotInitiator.selector);
wrapper.executeOperation({ asset: address(dai), amount: 1e18, fee: 0, initiator: address(0x666), params: "" });
}
}
4 changes: 2 additions & 2 deletions test/BalancerWrapper.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ contract BalancerWrapperTest is PRBTest, StdCheats {
}

function test_receiveFlashLoan_permissions() public {
vm.expectRevert("BalancerWrapper: not balancer");
vm.expectRevert(BalancerWrapper.NotBalancer.selector);
wrapper.receiveFlashLoan({
assets: address(dai).toArray(),
amounts: uint256(1e18).toArray(),
Expand All @@ -82,7 +82,7 @@ contract BalancerWrapperTest is PRBTest, StdCheats {
});

vm.prank(address(balancer));
vm.expectRevert("BalancerWrapper: params hash mismatch");
vm.expectRevert(BalancerWrapper.HashMismatch.selector);
wrapper.receiveFlashLoan({
assets: address(dai).toArray(),
amounts: uint256(1e18).toArray(),
Expand Down
2 changes: 1 addition & 1 deletion test/MockBorrower.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity ^0.8.19;

import "erc7399/IERC7399.sol";

Expand Down
Loading
Loading