Skip to content
9 changes: 5 additions & 4 deletions contracts/ERC4626Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,11 @@ contract ERC4626Adapter is ILiquidityPoolBase, AccessControl {
uint256 localBalance = token.balanceOf(address(this));
if (token == ASSETS) {
uint256 deposited = totalDeposited;
uint256 vaultBalance = TARGET_VAULT.maxWithdraw(address(this));
if (vaultBalance < deposited) return localBalance;
uint256 profit = vaultBalance - deposited;
TARGET_VAULT.withdraw(profit, address(this), address(this));
uint256 depositedShares = TARGET_VAULT.previewWithdraw(deposited);
uint256 totalShares = TARGET_VAULT.balanceOf(address(this));
if (totalShares <= depositedShares) return localBalance;
uint256 profit = TARGET_VAULT.redeem(totalShares - depositedShares, address(this), address(this));
assert(TARGET_VAULT.previewRedeem(depositedShares) >= deposited);
return profit + localBalance;
}
return localBalance;
Expand Down
4 changes: 4 additions & 0 deletions contracts/LiquidityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ contract LiquidityPool is ILiquidityPool, AccessControl, EIP712, ISigner {
// Allow native token transfers.
}

/// @notice The liqudity admin is supposed to call this function after transferring exact amount of assets.
/// Supplying amount less than the actual increase will result in the extra funds being treated as profit.
/// Supplying amount greater than the actual increase will result in the future profits treated as deposit.
function deposit(uint256 amount) external virtual override onlyRole(LIQUIDITY_ADMIN_ROLE) {
// called after receiving deposit in USDC
uint256 newBalance = ASSETS.balanceOf(address(this));
Expand Down Expand Up @@ -315,6 +318,7 @@ contract LiquidityPool is ILiquidityPool, AccessControl, EIP712, ISigner {
}

function setMPCAddress(address mpcAddress_) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(mpcAddress_ != address(0), ZeroAddress());
address oldMPCAddress = mpcAddress;
mpcAddress = mpcAddress_;
emit MPCAddressSet(oldMPCAddress, mpcAddress_);
Expand Down
6 changes: 3 additions & 3 deletions contracts/LiquidityPoolAave.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ contract LiquidityPoolAave is LiquidityPool {
error NothingToRepay();
error CollateralNotSupported();
error CannotWithdrawAToken();
error InvalidLength();

event SuppliedToAave(uint256 amount);
event BorrowTokenLTVSet(address token, uint256 oldLTV, uint256 newLTV);
Expand All @@ -70,8 +69,9 @@ contract LiquidityPoolAave is LiquidityPool {
AaveDataTypes.ReserveData memory collateralData = AAVE_POOL.getReserveData(address(liquidityToken));
ATOKEN = IERC20(collateralData.aTokenAddress);
IAavePoolDataProvider poolDataProvider = IAavePoolDataProvider(provider.getPoolDataProvider());
(,,,,,bool usageAsCollateralEnabled,,,,) = poolDataProvider.getReserveConfigurationData(liquidityToken);
require(usageAsCollateralEnabled, CollateralNotSupported());
(,,,,,bool usageAsCollateralEnabled,,,bool isActive, bool isFrozen) =
poolDataProvider.getReserveConfigurationData(liquidityToken);
require(usageAsCollateralEnabled && isActive && !isFrozen, CollateralNotSupported());
_setMinHealthFactor(minHealthFactor_);
_setDefaultLTV(defaultLTV_);
}
Expand Down
23 changes: 21 additions & 2 deletions contracts/PublicLiquidityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,23 @@ contract PublicLiquidityPool is LiquidityPool, ERC4626 {
return _virtualBalance - protocolFee;
}

function maxWithdraw(address owner) public view override returns (uint256) {
if (paused) {
return 0;
}
return Math.min(super.maxWithdraw(owner), ASSETS.balanceOf(address(this)));
}

function maxRedeem(address owner) public view override returns (uint256) {
if (paused) {
return 0;
}
return Math.min(
super.maxRedeem(owner),
_convertToShares(ASSETS.balanceOf(address(this)), Math.Rounding.Floor)
);
}

function _setProtocolFeeRate(uint16 protocolFeeRate_) internal {
require(protocolFeeRate_ <= RATE_DENOMINATOR, InvalidProtocolFeeRate());
protocolFeeRate = protocolFeeRate_;
Expand Down Expand Up @@ -149,10 +166,12 @@ contract PublicLiquidityPool is LiquidityPool, ERC4626 {
uint256 totalBalance = token.balanceOf(address(this));
if (token == ASSETS) {
uint256 profit = protocolFee;
uint256 virtualBalance = _virtualBalance;
protocolFee = 0;
if (totalBalance > _virtualBalance) {
_virtualBalance = (virtualBalance - profit).toUint128();
if (totalBalance > virtualBalance) {
// In case there are donations sent to the pool.
profit += totalBalance - _virtualBalance;
profit += totalBalance - virtualBalance;
}
return profit;
}
Expand Down
12 changes: 6 additions & 6 deletions contracts/Rebalancer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ contract Rebalancer is IRebalancer, AccessControlUpgradeable, CCTPAdapter {
function initialize(
address admin,
address rebalancer,
address[] memory pools,
Domain[] memory domains,
Provider[] memory providers
address[] calldata pools,
Domain[] calldata domains,
Provider[] calldata providers
) external initializer() {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(REBALANCER_ROLE, rebalancer);
Expand All @@ -90,9 +90,9 @@ contract Rebalancer is IRebalancer, AccessControlUpgradeable, CCTPAdapter {
}

function _setRoute(
address[] memory pools,
Domain[] memory domains,
Provider[] memory providers,
address[] calldata pools,
Domain[] calldata domains,
Provider[] calldata providers,
bool isAllowed
) internal {
RebalancerStorage storage $ = _getStorage();
Expand Down
29 changes: 15 additions & 14 deletions contracts/Repayer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ contract Repayer is
error ZeroAmount();
error InsufficientBalance();
error RouteDenied();
error InvalidRoute();
error InvalidToken();
error UnsupportedProvider();
error InvalidPoolAssets();
Expand Down Expand Up @@ -97,6 +96,7 @@ contract Repayer is
DOMAIN = localDomain;
ASSETS = assets;
WRAPPED_NATIVE_TOKEN = IWrappedNativeToken(wrappedNativeToken);
_disableInitializers();
}

receive() external payable {
Expand All @@ -106,10 +106,10 @@ contract Repayer is
function initialize(
address admin,
address repayer,
address[] memory pools,
Domain[] memory domains,
Provider[] memory providers,
bool[] memory poolSupportsAllTokens
address[] calldata pools,
Domain[] calldata domains,
Provider[] calldata providers,
bool[] calldata poolSupportsAllTokens
) external initializer() {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(REPAYER_ROLE, repayer);
Expand All @@ -120,7 +120,7 @@ contract Repayer is
address[] calldata pools,
Domain[] calldata domains,
Provider[] calldata providers,
bool[] memory poolSupportsAllTokens,
bool[] calldata poolSupportsAllTokens,
bool isAllowed
) external onlyRole(DEFAULT_ADMIN_ROLE) {
_setRoute(pools, domains, providers, poolSupportsAllTokens, isAllowed);
Expand All @@ -129,6 +129,7 @@ contract Repayer is
/// @notice If the selected provider requires native currency payment to cover fees,
/// then caller has to include it in the transaction. It is then the responsibility
/// of the Adapter to forward the payment and return any change back to the caller.
/// @dev Adapters are responsible for revoking unused allowance if necessary.
function initiateRepay(
IERC20 token,
uint256 amount,
Expand All @@ -150,14 +151,14 @@ contract Repayer is

RepayerStorage storage $ = _getStorage();

if (!$.poolSupportsAllTokens[destinationPool]) {
require(token == ASSETS, InvalidToken());
}

if (provider == Provider.LOCAL) {
// This should always pass because isRouteAllowed check will fail earlier.
// It is put here for explicitness.
require(destinationDomain == DOMAIN, UnsupportedDomain());

if (!$.poolSupportsAllTokens[destinationPool]) {
require(token == ASSETS, InvalidToken());
}
// For local we proceed to the process right away.
_processRepayLOCAL(token, amount, destinationPool);
} else
Expand Down Expand Up @@ -210,10 +211,10 @@ contract Repayer is
}

function _setRoute(
address[] memory pools,
Domain[] memory domains,
Provider[] memory providers,
bool[] memory poolSupportsAllTokens,
address[] calldata pools,
Domain[] calldata domains,
Provider[] calldata providers,
bool[] calldata poolSupportsAllTokens,
bool isAllowed
) internal {
RepayerStorage storage $ = _getStorage();
Expand Down
2 changes: 1 addition & 1 deletion contracts/utils/AcrossAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ abstract contract AcrossAdapter is AdapterHelper {
address destinationPool,
Domain destinationDomain,
bytes calldata extraData
) internal {
) internal notPayable {
require(address(ACROSS_SPOKE_POOL) != address(0), ZeroAddress());
token.forceApprove(address(ACROSS_SPOKE_POOL), amount);
(
Expand Down
9 changes: 9 additions & 0 deletions contracts/utils/AdapterHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import {IRoute} from ".././interfaces/IRoute.sol";

abstract contract AdapterHelper is IRoute {
error SlippageTooHigh();
error NotPayable();

modifier notPayable() {
require(msg.value == 0, NotPayable());
_;
}

function domainChainId(Domain destinationDomain) public pure virtual returns (uint32) {
if (destinationDomain == Domain.ETHEREUM) {
Expand All @@ -25,6 +31,9 @@ abstract contract AdapterHelper is IRoute {
if (destinationDomain == Domain.POLYGON_MAINNET) {
return 137;
} else
if (destinationDomain == Domain.UNICHAIN) {
return 130;
} else
if (destinationDomain == Domain.BSC) {
return 56;
} else
Expand Down
2 changes: 1 addition & 1 deletion contracts/utils/CCTPAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ abstract contract CCTPAdapter is AdapterHelper {
uint256 amount,
address destinationPool,
Domain destinationDomain
) internal {
) internal notPayable {
token.forceApprove(address(CCTP_TOKEN_MESSENGER), amount);
CCTP_TOKEN_MESSENGER.depositForBurnWithCaller(
amount,
Expand Down
6 changes: 5 additions & 1 deletion contracts/utils/OptimismStandardBridgeAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,24 @@ abstract contract OptimismStandardBridgeAdapter is AdapterHelper {
Domain destinationDomain,
bytes calldata extraData,
Domain localDomain
) internal {
) internal notPayable{
// We are only interested in fast L1->L2 bridging, because the reverse is slow.
require(
localDomain == Domain.ETHEREUM && destinationDomain == Domain.OP_MAINNET,
UnsupportedDomain()
);
require(address(OPTIMISM_STANDARD_BRIDGE) != address(0), ZeroAddress());
// WARNING: Contract doesn't maintain an input/output token mapping which could result in a mismatch.
// If the output token is wrong then the input tokens will be lost.
// Notice: In case of minGasLimit being too low, the message could be retried on the destination chain.
(address outputToken, uint32 minGasLimit) = abi.decode(extraData, (address, uint32));
if (token == WRAPPED_NATIVE_TOKEN) {
WRAPPED_NATIVE_TOKEN.withdraw(amount);
OPTIMISM_STANDARD_BRIDGE.bridgeETHTo{value: amount}(destinationPool, minGasLimit, "");
return;
}

require(outputToken != address(0), ZeroAddress());
token.forceApprove(address(OPTIMISM_STANDARD_BRIDGE), amount);
OPTIMISM_STANDARD_BRIDGE.bridgeERC20To(
address(token),
Expand Down
1 change: 1 addition & 0 deletions contracts/utils/StargateAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ abstract contract StargateAdapter is AdapterHelper {
oftCmd: new bytes(1)
});

// The caller is responsible for estimating and providing the correct messaging fee.
MessagingFee memory messagingFee = MessagingFee(msg.value, 0);

(
Expand Down
Loading