Skip to content

Commit

Permalink
Use the Registry to initialize the uniswap wrapper (#15)
Browse files Browse the repository at this point in the history
* use the Registry to initialize the uniswap wrapper

* Make assets immutable

* Lint

* Deploy script

* Lint 2

* Standardise errors

---------

Co-authored-by: alcueca <[email protected]>
Co-authored-by: ultrasecr.eth <[email protected]>
  • Loading branch information
3 people authored Aug 9, 2023
1 parent b34d05c commit b94b222
Show file tree
Hide file tree
Showing 26 changed files with 117 additions and 48 deletions.
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

0 comments on commit b94b222

Please sign in to comment.