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

Add Zap compliant with Liquidity Provider Cap #32

Merged
merged 3 commits into from
Oct 10, 2024
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
34 changes: 30 additions & 4 deletions src/contracts/AbstractARM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,10 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
address public feeCollector;
/// @notice The address of the CapManager contract used to manage the ARM's liquidity provider and total assets caps
address public capManager;
/// @notice The address of the Zapper contract that converts ETH to WETH before ARM deposits
address public zap;

uint256[43] private _gap;
uint256[41] private _gap;

////////////////////////////////////////////////////
/// Events
Expand All @@ -121,6 +123,7 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
event FeeUpdated(uint256 fee);
event FeeCollectorUpdated(address indexed newFeeCollector);
event CapManagerUpdated(address indexed capManager);
event ZapUpdated(address indexed zap);

constructor(address _token0, address _token1, address _liquidityAsset, uint256 _claimDelay) {
require(IERC20(_token0).decimals() == 18);
Expand Down Expand Up @@ -424,6 +427,22 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
/// @param assets The amount of liquidity assets to deposit
/// @return shares The amount of shares that were minted
function deposit(uint256 assets) external returns (uint256 shares) {
shares = _deposit(assets, msg.sender);
}

/// @notice deposit liquidity assets in exchange for liquidity provider (LP) shares.
/// This function is restricted to the Zap contract.
/// @param assets The amount of liquidity assets to deposit
/// @param liquidityProvider The address of the liquidity provider
/// @return shares The amount of shares that were minted
function deposit(uint256 assets, address liquidityProvider) external returns (uint256 shares) {
require(msg.sender == zap, "Only Zap");

shares = _deposit(assets, liquidityProvider);
}

/// @dev Internal logic for depositing liquidity assets in exchange for liquidity provider (LP) shares.
function _deposit(uint256 assets, address liquidityProvider) internal returns (uint256 shares) {
// Calculate the amount of shares to mint after the performance fees have been accrued
// which reduces the available assets, and before new assets are deposited.
shares = convertToShares(assets);
Expand All @@ -432,17 +451,17 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
IERC20(liquidityAsset).transferFrom(msg.sender, address(this), assets);

// mint shares
_mint(msg.sender, shares);
_mint(liquidityProvider, shares);

// Add the deposited assets to the last available assets
lastAvailableAssets += SafeCast.toInt128(SafeCast.toInt256(assets));

// Check the liquidity provider caps after the new assets have been deposited
if (capManager != address(0)) {
ICapManager(capManager).postDepositHook(msg.sender, assets);
ICapManager(capManager).postDepositHook(liquidityProvider, assets);
}

emit Deposit(msg.sender, assets, shares);
emit Deposit(liquidityProvider, assets, shares);
}

/// @notice Preview the amount of assets that would be received for burning a given amount of shares
Expand Down Expand Up @@ -599,6 +618,13 @@ abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
emit CapManagerUpdated(_capManager);
}

/// @notice Set the Zap contract address.
function setZap(address _zap) external onlyOwner {
zap = _zap;

emit ZapUpdated(_zap);
}

////////////////////////////////////////////////////
/// Performance Fee Functions
////////////////////////////////////////////////////
Expand Down
1 change: 1 addition & 0 deletions src/contracts/Interfaces.sol
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ interface IOethARM {
interface ILiquidityProviderARM is IERC20 {
function previewDeposit(uint256 assets) external returns (uint256 shares);
function deposit(uint256 assets) external returns (uint256 shares);
function deposit(uint256 assets, address liquidityProvider) external returns (uint256 shares);

function previewRedeem(uint256 shares) external returns (uint256 assets);
function requestRedeem(uint256 shares) external returns (uint256 requestId, uint256 assets);
Expand Down
5 changes: 1 addition & 4 deletions src/contracts/ZapperLidoARM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ contract ZapperLidoARM is Ownable {
weth.deposit{value: ethBalance}();

// Deposit all WETH to LidoARM
shares = lidoArm.deposit(ethBalance);

// Transfer received ARM LP shares to msg.sender
lidoArm.transfer(msg.sender, shares);
shares = lidoArm.deposit(ethBalance, msg.sender);

// Emit event
emit Zap(msg.sender, ethBalance, shares);
Expand Down
13 changes: 11 additions & 2 deletions test/fork/Zapper/Deposit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pragma solidity ^0.8.23;
import {Fork_Shared_Test_} from "test/fork/shared/Shared.sol";

// Contracts
import {CapManager} from "src/contracts/CapManager.sol";
import {ZapperLidoARM} from "contracts/ZapperLidoARM.sol";

contract Fork_Concrete_ZapperLidoARM_Deposit_Test_ is Fork_Shared_Test_ {
Expand All @@ -14,23 +15,30 @@ contract Fork_Concrete_ZapperLidoARM_Deposit_Test_ is Fork_Shared_Test_ {
vm.deal(address(this), DEFAULT_AMOUNT);
}

function test_Deposit_ViaFunction() public setLiquidityProviderCap(address(zapperLidoARM), DEFAULT_AMOUNT) {
function test_Deposit_ViaFunction() public {
assertEq(lidoARM.balanceOf(address(this)), 0);
uint256 expectedShares = lidoARM.previewDeposit(DEFAULT_AMOUNT);
uint256 capBefore = capManager.liquidityProviderCaps(address(this));

vm.expectEmit({emitter: address(capManager)});
emit CapManager.LiquidityProviderCap(address(this), capBefore - DEFAULT_AMOUNT);
vm.expectEmit({emitter: address(zapperLidoARM)});
emit ZapperLidoARM.Zap(address(this), DEFAULT_AMOUNT, expectedShares);
// Deposit
zapperLidoARM.deposit{value: DEFAULT_AMOUNT}();

// Check balance
assertEq(lidoARM.balanceOf(address(this)), DEFAULT_AMOUNT);
assertEq(capManager.liquidityProviderCaps(address(this)), capBefore - DEFAULT_AMOUNT);
}

function test_Deposit_ViaCall() public setLiquidityProviderCap(address(zapperLidoARM), DEFAULT_AMOUNT) {
function test_Deposit_ViaCall() public {
assertEq(lidoARM.balanceOf(address(this)), 0);
uint256 expectedShares = lidoARM.previewDeposit(DEFAULT_AMOUNT);
uint256 capBefore = capManager.liquidityProviderCaps(address(this));

vm.expectEmit({emitter: address(capManager)});
emit CapManager.LiquidityProviderCap(address(this), capBefore - DEFAULT_AMOUNT);
vm.expectEmit({emitter: address(zapperLidoARM)});
emit ZapperLidoARM.Zap(address(this), DEFAULT_AMOUNT, expectedShares);
// Deposit
Expand All @@ -39,5 +47,6 @@ contract Fork_Concrete_ZapperLidoARM_Deposit_Test_ is Fork_Shared_Test_ {

// Check balance
assertEq(lidoARM.balanceOf(address(this)), DEFAULT_AMOUNT);
assertEq(capManager.liquidityProviderCaps(address(this)), capBefore - DEFAULT_AMOUNT);
}
}
3 changes: 3 additions & 0 deletions test/fork/shared/Shared.sol
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ abstract contract Fork_Shared_Test_ is Modifiers {

// --- Deploy ZapperLidoARM ---
zapperLidoARM = new ZapperLidoARM(address(weth), address(lidoProxy));

// Set zap address in LidoARM.
lidoARM.setZap(address(zapperLidoARM));
}

function _label() internal {
Expand Down