Skip to content
Draft
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
24 changes: 14 additions & 10 deletions src/contracts/AbstractARM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ pragma solidity ^0.8.23;

import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC20 as OZIERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";

import {OwnableOperable} from "./OwnableOperable.sol";
Expand All @@ -17,6 +19,8 @@ import {IAssetAdapter, IERC20, ICapManager} from "./Interfaces.sol";
* @author Origin Protocol Inc
*/
abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
using SafeERC20 for OZIERC20;

////////////////////////////////////////////////////
/// Constants
////////////////////////////////////////////////////
Expand Down Expand Up @@ -213,7 +217,7 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
__ERC20_init(_name, _symbol);

// Transfer a small bit of liquidity from the initializer to this contract.
IERC20(liquidityAsset).transferFrom(msg.sender, address(this), MIN_TOTAL_SUPPLY);
OZIERC20(liquidityAsset).safeTransferFrom(msg.sender, address(this), MIN_TOTAL_SUPPLY);
// Mint a small amount of shares to a dead account so total supply can never be zero.
// This avoids donation attacks when there are no assets in the ARM contract.
_mint(DEAD_ACCOUNT, MIN_TOTAL_SUPPLY);
Expand Down Expand Up @@ -369,10 +373,10 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
_consumeSwapLiquidityLimit(config, isBuySide, amountOut);

// Transfer the input tokens from the caller to this ARM contract
inToken.transferFrom(msg.sender, address(this), amountIn);
OZIERC20(address(inToken)).safeTransferFrom(msg.sender, address(this), amountIn);

// Transfer the output tokens to the recipient
outToken.transfer(to, amountOut);
OZIERC20(address(outToken)).safeTransfer(to, amountOut);
}

/// @dev Swap for exact output between the liquidity asset and one supported base asset.
Expand Down Expand Up @@ -412,10 +416,10 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
_consumeSwapLiquidityLimit(config, isBuySide, amountOut);

// Transfer the input tokens from the caller to this ARM contract
inToken.transferFrom(msg.sender, address(this), amountIn);
OZIERC20(address(inToken)).safeTransferFrom(msg.sender, address(this), amountIn);

// Transfer the output tokens to the recipient
outToken.transfer(to, amountOut);
OZIERC20(address(outToken)).safeTransfer(to, amountOut);
}

/// @dev Resolve the supported base asset from a 2-token swap pair.
Expand Down Expand Up @@ -536,7 +540,7 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {

baseAssets.push(newBaseAsset);
// Allow the adapter to pull base assets when requesting protocol redemptions.
IERC20(newBaseAsset).approve(adapter, type(uint256).max);
OZIERC20(newBaseAsset).forceApprove(adapter, type(uint256).max);
baseAssetConfigs[newBaseAsset] = BaseAssetConfig({
buyPrice: SafeCast.toUint128(buyPrice),
sellPrice: SafeCast.toUint128(sellPrice),
Expand Down Expand Up @@ -681,7 +685,7 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
shares = convertToShares(assets);

// Transfer liquidity from the depositor before minting LP shares.
IERC20(liquidityAsset).transferFrom(msg.sender, address(this), assets);
OZIERC20(liquidityAsset).safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);

// Enforce LP caps after the deposit has changed the receiver's share balance.
Expand Down Expand Up @@ -774,7 +778,7 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
}

// Transfer the liquidity asset to the withdrawer.
IERC20(liquidityAsset).transfer(msg.sender, assets);
OZIERC20(liquidityAsset).safeTransfer(msg.sender, assets);
emit RedeemClaimed(msg.sender, requestId, assets);
}

Expand Down Expand Up @@ -960,7 +964,7 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
require(fees <= IERC20(liquidityAsset).balanceOf(address(this)), "ARM: insufficient liquidity");

feesAccrued = 0;
IERC20(liquidityAsset).transfer(feeCollector, fees);
OZIERC20(liquidityAsset).safeTransfer(feeCollector, fees);
emit FeeCollected(feeCollector, fees);
}

Expand Down Expand Up @@ -1060,7 +1064,7 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
if (targetLiquidityDelta > allocateThreshold) {
// We have too much liquidity in the ARM, so deposit some to the active lending market.
uint256 depositAmount = SafeCast.toUint256(targetLiquidityDelta);
IERC20(liquidityAsset).approve(activeMarketMem, depositAmount);
OZIERC20(liquidityAsset).forceApprove(activeMarketMem, depositAmount);
IERC4626(activeMarketMem).deposit(depositAmount, address(this));
actualLiquidityDelta = SafeCast.toInt256(depositAmount);
} else if (targetLiquidityDelta < 0) {
Expand Down
10 changes: 7 additions & 3 deletions src/contracts/adapters/AbstractLidoAssetAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
pragma solidity ^0.8.23;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IERC20 as OZIERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {IAssetAdapter, IERC20, IStETHWithdrawal, ISTETH, IWETH} from "../Interfaces.sol";

Expand All @@ -10,6 +12,8 @@ interface ILidoARMLegacyQueueCheck {
}

abstract contract AbstractLidoAssetAdapter is Initializable, IAssetAdapter {
using SafeERC20 for OZIERC20;

uint256 internal constant MAX_WITHDRAWAL_AMOUNT = 1000 ether;

address public immutable arm;
Expand All @@ -29,12 +33,12 @@ abstract contract AbstractLidoAssetAdapter is Initializable, IAssetAdapter {
steth = ISTETH(_steth);
lidoWithdrawalQueue = IStETHWithdrawal(_lidoWithdrawalQueue);

IERC20(_steth).approve(_lidoWithdrawalQueue, type(uint256).max);
OZIERC20(_steth).forceApprove(_lidoWithdrawalQueue, type(uint256).max);
}

function initialize() external initializer {
ILidoARMLegacyQueueCheck(arm).checkNoLegacyLidoWithdrawalRequests();
IERC20(address(steth)).approve(address(lidoWithdrawalQueue), type(uint256).max);
OZIERC20(address(steth)).forceApprove(address(lidoWithdrawalQueue), type(uint256).max);
}

function asset() external view returns (address) {
Expand Down Expand Up @@ -115,7 +119,7 @@ abstract contract AbstractLidoAssetAdapter is Initializable, IAssetAdapter {
}

assetsReceived = weth.balanceOf(address(this)) - wethBefore;
IERC20(address(weth)).transfer(arm, assetsReceived);
OZIERC20(address(weth)).safeTransfer(arm, assetsReceived);
}

function claimableRedeem() external view returns (uint256 claimableShares, uint256 claimableAssets) {
Expand Down
9 changes: 7 additions & 2 deletions src/contracts/adapters/EthenaAssetAdapter.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;

import {IERC20 as OZIERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {EthenaUnstaker} from "../EthenaUnstaker.sol";
import {IAssetAdapter, IERC20, IStakedUSDe, UserCooldown} from "../Interfaces.sol";
import {Ownable} from "../Ownable.sol";

contract EthenaAssetAdapter is IAssetAdapter, Ownable {
using SafeERC20 for OZIERC20;

uint256 public constant DELAY_REQUEST = 30 minutes;
uint8 public constant MAX_UNSTAKERS = 42;

Expand Down Expand Up @@ -61,7 +66,7 @@ contract EthenaAssetAdapter is IAssetAdapter, Ownable {
pendingUnstakerIndexes.push(nextUnstakerIndex);
nextUnstakerIndex = uint8((nextUnstakerIndex + 1) % MAX_UNSTAKERS);

susde.transferFrom(arm, unstaker, shares);
OZIERC20(address(susde)).safeTransferFrom(arm, unstaker, shares);
assetsExpected = EthenaUnstaker(unstaker).requestUnstake(shares);
requestShares[unstaker] = shares;
requestAssets[unstaker] = assetsExpected;
Expand Down Expand Up @@ -101,7 +106,7 @@ contract EthenaAssetAdapter is IAssetAdapter, Ownable {
nextPendingIndex = cursor + claimCount;

assetsReceived = usde.balanceOf(address(this)) - balanceBefore;
usde.transfer(arm, assetsReceived);
OZIERC20(address(usde)).safeTransfer(arm, assetsReceived);
}

function deployUnstakers() external onlyOwner {
Expand Down
12 changes: 8 additions & 4 deletions src/contracts/adapters/EtherFiAssetAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ pragma solidity ^0.8.23;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC20 as OZIERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {IAssetAdapter, IERC20, IEETHWithdrawal, IEETHWithdrawalNFT, IWETH} from "../Interfaces.sol";

contract EtherFiAssetAdapter is Initializable, IAssetAdapter, IERC721Receiver {
using SafeERC20 for OZIERC20;

address public immutable arm;
IERC20 public immutable eeth;
IWETH public immutable weth;
Expand All @@ -30,11 +34,11 @@ contract EtherFiAssetAdapter is Initializable, IAssetAdapter, IERC721Receiver {
etherfiWithdrawalQueue = IEETHWithdrawal(_etherfiWithdrawalQueue);
etherfiWithdrawalNFT = IEETHWithdrawalNFT(_etherfiWithdrawalNFT);

eeth.approve(_etherfiWithdrawalQueue, type(uint256).max);
OZIERC20(address(eeth)).forceApprove(_etherfiWithdrawalQueue, type(uint256).max);
}

function initialize() external initializer {
eeth.approve(address(etherfiWithdrawalQueue), type(uint256).max);
OZIERC20(address(eeth)).forceApprove(address(etherfiWithdrawalQueue), type(uint256).max);
}

function asset() external view returns (address) {
Expand All @@ -55,7 +59,7 @@ contract EtherFiAssetAdapter is Initializable, IAssetAdapter, IERC721Receiver {
nonZeroShares(shares)
returns (uint256 sharesRequested, uint256 assetsExpected)
{
eeth.transferFrom(arm, address(this), shares);
OZIERC20(address(eeth)).safeTransferFrom(arm, address(this), shares);
uint256 requestId = etherfiWithdrawalQueue.requestWithdraw(address(this), shares);
requestShares[requestId] = shares;
pendingRequestIds.push(requestId);
Expand Down Expand Up @@ -101,7 +105,7 @@ contract EtherFiAssetAdapter is Initializable, IAssetAdapter, IERC721Receiver {
if (ethBalance > 0) weth.deposit{value: ethBalance}();

assetsReceived = weth.balanceOf(address(this)) - wethBefore;
IERC20(address(weth)).transfer(arm, assetsReceived);
OZIERC20(address(weth)).safeTransfer(arm, assetsReceived);
}

function pendingRequestIdsLength() external view returns (uint256) {
Expand Down
12 changes: 8 additions & 4 deletions src/contracts/adapters/OriginAssetAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
pragma solidity ^0.8.23;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IERC20 as OZIERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {IAssetAdapter, IERC20, IOriginVault} from "../Interfaces.sol";

contract OriginAssetAdapter is Initializable, IAssetAdapter {
using SafeERC20 for OZIERC20;

address public immutable arm;
IERC20 public immutable otoken;
IERC20 public immutable liquidityAsset;
Expand All @@ -20,11 +24,11 @@ contract OriginAssetAdapter is Initializable, IAssetAdapter {
otoken = IERC20(_otoken);
liquidityAsset = IERC20(_liquidityAsset);
vault = IOriginVault(_vault);
otoken.approve(_vault, type(uint256).max);
OZIERC20(address(otoken)).forceApprove(_vault, type(uint256).max);
}

function initialize() external initializer {
otoken.approve(address(vault), type(uint256).max);
OZIERC20(address(otoken)).forceApprove(address(vault), type(uint256).max);
}

function asset() external view returns (address) {
Expand All @@ -45,7 +49,7 @@ contract OriginAssetAdapter is Initializable, IAssetAdapter {
nonZeroShares(shares)
returns (uint256 sharesRequested, uint256 assetsExpected)
{
otoken.transferFrom(arm, address(this), shares);
OZIERC20(address(otoken)).safeTransferFrom(arm, address(this), shares);
(uint256 requestId,) = vault.requestWithdrawal(shares);
requestShares[requestId] = shares;
pendingRequestIds.push(requestId);
Expand Down Expand Up @@ -88,7 +92,7 @@ contract OriginAssetAdapter is Initializable, IAssetAdapter {
(, uint256 amountClaimed) = vault.claimWithdrawals(requestIds);
uint256 balanceDelta = liquidityAsset.balanceOf(address(this)) - balanceBefore;
assetsReceived = balanceDelta > amountClaimed ? balanceDelta : amountClaimed;
liquidityAsset.transfer(arm, balanceDelta);
OZIERC20(address(liquidityAsset)).safeTransfer(arm, balanceDelta);
}

function pendingRequestIdsLength() external view returns (uint256) {
Expand Down
8 changes: 6 additions & 2 deletions src/contracts/adapters/StETHAssetAdapter.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;

import {IERC20} from "../Interfaces.sol";
import {IERC20 as OZIERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {AbstractLidoAssetAdapter} from "./AbstractLidoAssetAdapter.sol";

contract StETHAssetAdapter is AbstractLidoAssetAdapter {
using SafeERC20 for OZIERC20;

constructor(address _arm, address _weth, address _steth, address _lidoWithdrawalQueue)
AbstractLidoAssetAdapter(_arm, _weth, _steth, _lidoWithdrawalQueue)
{}
Expand All @@ -18,7 +22,7 @@ contract StETHAssetAdapter is AbstractLidoAssetAdapter {
}

function _pullSharesAndConvertToSteth(address owner, uint256 shares) internal override returns (uint256 assetsOut) {
IERC20(address(steth)).transferFrom(owner, address(this), shares);
OZIERC20(address(steth)).safeTransferFrom(owner, address(this), shares);
assetsOut = shares;
}

Expand Down
12 changes: 8 additions & 4 deletions src/contracts/adapters/WeETHAssetAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ pragma solidity ^0.8.23;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC20 as OZIERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {IAssetAdapter, IERC20, IEETHWithdrawal, IEETHWithdrawalNFT, IWeETH, IWETH} from "../Interfaces.sol";

contract WeETHAssetAdapter is Initializable, IAssetAdapter, IERC721Receiver {
using SafeERC20 for OZIERC20;

address public immutable arm;
IWeETH public immutable weeth;
IERC20 public immutable eeth;
Expand Down Expand Up @@ -34,11 +38,11 @@ contract WeETHAssetAdapter is Initializable, IAssetAdapter, IERC721Receiver {
etherfiWithdrawalQueue = IEETHWithdrawal(_etherfiWithdrawalQueue);
etherfiWithdrawalNFT = IEETHWithdrawalNFT(_etherfiWithdrawalNFT);

eeth.approve(_etherfiWithdrawalQueue, type(uint256).max);
OZIERC20(address(eeth)).forceApprove(_etherfiWithdrawalQueue, type(uint256).max);
}

function initialize() external initializer {
eeth.approve(address(etherfiWithdrawalQueue), type(uint256).max);
OZIERC20(address(eeth)).forceApprove(address(etherfiWithdrawalQueue), type(uint256).max);
}

function asset() external view returns (address) {
Expand All @@ -59,7 +63,7 @@ contract WeETHAssetAdapter is Initializable, IAssetAdapter, IERC721Receiver {
nonZeroShares(shares)
returns (uint256 sharesRequested, uint256 assetsExpected)
{
IERC20(address(weeth)).transferFrom(arm, address(this), shares);
OZIERC20(address(weeth)).safeTransferFrom(arm, address(this), shares);
assetsExpected = weeth.unwrap(shares);
uint256 requestId = etherfiWithdrawalQueue.requestWithdraw(address(this), assetsExpected);

Expand Down Expand Up @@ -108,7 +112,7 @@ contract WeETHAssetAdapter is Initializable, IAssetAdapter, IERC721Receiver {
if (ethBalance > 0) weth.deposit{value: ethBalance}();

assetsReceived = weth.balanceOf(address(this)) - wethBefore;
IERC20(address(weth)).transfer(arm, assetsReceived);
OZIERC20(address(weth)).safeTransfer(arm, assetsReceived);
}

function pendingRequestIdsLength() external view returns (uint256) {
Expand Down
12 changes: 8 additions & 4 deletions src/contracts/adapters/WrappedOriginAssetAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ pragma solidity ^0.8.23;

import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IERC20 as OZIERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {IAssetAdapter, IERC20, IOriginVault} from "../Interfaces.sol";

contract WrappedOriginAssetAdapter is Initializable, IAssetAdapter {
using SafeERC20 for OZIERC20;

address public immutable arm;
IERC4626 public immutable wrappedOToken;
IERC20 public immutable otoken;
Expand All @@ -24,11 +28,11 @@ contract WrappedOriginAssetAdapter is Initializable, IAssetAdapter {
otoken = IERC20(_otoken);
liquidityAsset = IERC20(_liquidityAsset);
vault = IOriginVault(_vault);
otoken.approve(_vault, type(uint256).max);
OZIERC20(address(otoken)).forceApprove(_vault, type(uint256).max);
}

function initialize() external initializer {
otoken.approve(address(vault), type(uint256).max);
OZIERC20(address(otoken)).forceApprove(address(vault), type(uint256).max);
}

function asset() external view returns (address) {
Expand All @@ -49,7 +53,7 @@ contract WrappedOriginAssetAdapter is Initializable, IAssetAdapter {
nonZeroShares(shares)
returns (uint256 sharesRequested, uint256 assetsExpected)
{
IERC20(address(wrappedOToken)).transferFrom(arm, address(this), shares);
OZIERC20(address(wrappedOToken)).safeTransferFrom(arm, address(this), shares);
assetsExpected = wrappedOToken.redeem(shares, address(this), address(this));
(uint256 requestId,) = vault.requestWithdrawal(assetsExpected);

Expand Down Expand Up @@ -95,7 +99,7 @@ contract WrappedOriginAssetAdapter is Initializable, IAssetAdapter {
(, uint256 amountClaimed) = vault.claimWithdrawals(requestIds);
uint256 balanceDelta = liquidityAsset.balanceOf(address(this)) - balanceBefore;
assetsReceived = balanceDelta > amountClaimed ? balanceDelta : amountClaimed;
liquidityAsset.transfer(arm, balanceDelta);
OZIERC20(address(liquidityAsset)).safeTransfer(arm, balanceDelta);
}

function pendingRequestIdsLength() external view returns (uint256) {
Expand Down
Loading
Loading