Skip to content

Commit

Permalink
connectors: add min amount out parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
lgalende committed Nov 22, 2023
1 parent fc5da61 commit 13dffe8
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,36 @@ interface IERC4626Connector {
/**
* @dev The amount deposited is lower than the expected amount
*/
error ERC4626InvalidDeposit(uint256 actual, uint256 expected);
error ERC4626BadSharesOut(uint256 shares, uint256 minSharesOut);

/**
* @dev The amount redeemed is lower than the expected amount
*/
error ERC4626InvalidRedeem(uint256 actual, uint256 expected);
error ERC4626BadAssetsOut(uint256 assets, uint256 minAssetsOut);

/**
* @dev Tells the underlying token of an ERC4626
*/
function getToken(address erc4626) external view returns (address);

/**
* @dev Deposits assets to the underlying ERC4626
* @param erc4626 Address of the ERC4626 to join
* @param tokenIn Address of the token to join the ERC4626
* @param assets Amount of assets to be deposited
* @param minSharesOut Minimum amount of shares willing to receive
*/
function join(address erc4626, address tokenIn, uint256 assets)
function join(address erc4626, address tokenIn, uint256 assets, uint256 minSharesOut)
external
returns (address tokenOut, uint256 depositedShares);
returns (address tokenOut, uint256 shares);

/**
* @dev Withdtaws assets from the underlying ERC4626
* @param erc4626 Address of the ERC4626 to exit
* @param shares Amount of shares to be redeemed
* @param minAssetsOut Minimum amount of assets willing to receive
*/
function exit(address erc4626, uint256 shares) external returns (address token, uint256 redeemedAssets);
function exit(address erc4626, uint256 shares, uint256 minAssetsOut)
external
returns (address token, uint256 assets);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,45 +24,49 @@ import '../../interfaces/liquidity/erc4626/IERC4626Connector.sol';
* @title ERC4626Connector
*/
contract ERC4626Connector is IERC4626Connector {
/**
* @dev Tells the underlying token of an ERC4626
*/
function getToken(address erc4626) public view returns (address) {
return IERC4626(erc4626).asset();
}

/**
* @dev Deposits assets to an ERC4626
* @param erc4626 Address of the ERC4626 to join
* @param tokenIn Address of the token to join the ERC4626
* @param assets Amount of assets to be deposited
* @param minSharesOut Minimum amount of shares willing to receive
*/
function join(address erc4626, address tokenIn, uint256 assets)
function join(address erc4626, address tokenIn, uint256 assets, uint256 minSharesOut)
external
override
returns (address tokenOut, uint256 depositedShares)
returns (address tokenOut, uint256 shares)
{
tokenOut = erc4626;
if (assets == 0) return (tokenOut, 0);
address expectedTokenIn = getToken(erc4626);
if (tokenIn != expectedTokenIn) revert ERC4626InvalidToken(tokenIn, expectedTokenIn);

uint256 shares = IERC4626(erc4626).convertToShares(assets);
ERC20Helpers.approve(tokenIn, erc4626, assets);
depositedShares = IERC4626(erc4626).deposit(assets, address(this));
if (depositedShares < shares) revert ERC4626InvalidDeposit(depositedShares, shares);
shares = IERC4626(erc4626).deposit(assets, address(this));
if (shares < minSharesOut) revert ERC4626BadSharesOut(shares, minSharesOut);
}

/**
* @dev Withdtaws assets from an ERC4626
* @param erc4626 Address of the ERC4626 to exit
* @param shares Amount of shares to be redeemed
* @param minAssetsOut Minimum amount of assets willing to receive
*/
function exit(address erc4626, uint256 shares) external override returns (address token, uint256 redeemedAssets) {
function exit(address erc4626, uint256 shares, uint256 minAssetsOut)
external
override
returns (address token, uint256 assets)
{
token = getToken(erc4626);
if (shares == 0) return (token, 0);
uint256 assets = IERC4626(erc4626).convertToAssets(shares);
redeemedAssets = IERC4626(erc4626).redeem(shares, address(this), address(this));
if (redeemedAssets < assets) revert ERC4626InvalidRedeem(redeemedAssets, assets);
}

/**
* @dev Tells the underlying token of an ERC4626
*/
function getToken(address erc4626) internal view returns (address) {
return IERC4626(erc4626).asset();
assets = IERC4626(erc4626).redeem(shares, address(this), address(this));
if (assets < minAssetsOut) revert ERC4626BadAssetsOut(assets, minAssetsOut);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import { Contract } from 'ethers'

/* eslint-disable no-secrets/no-secrets */

const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'

const WHALE = '0xf584f8728b874a6a5c7a8d4d387c9aae9172d621'
const MORPHO_AAVE_V3_WETH = '0x39Dd7790e75C6F663731f7E1FdC0f35007D3879b'

describe('ERC4626Connector', function () {
describe('ERC4626Connector', () => {
let whale: SignerWithAddress
let connector: Contract, erc4626Adapter: Contract, weth: Contract

Expand All @@ -46,6 +46,10 @@ describe('ERC4626Connector', function () {
weth = await instanceAt('IERC20', WETH)
})

it('returns the underlying asset', async () => {
expect(await connector.getToken(erc4626Adapter.address)).to.be.equal(WETH)
})

context('when the token in is the underlying token', () => {
it('joins the connector', async () => {
const tokenIn = weth.address
Expand All @@ -55,7 +59,7 @@ describe('ERC4626Connector', function () {
const previousWethBalance = await weth.balanceOf(connector.address)
const previousShares = await erc4626Adapter.balanceOf(connector.address)

await connector.join(erc4626Adapter.address, tokenIn, JOIN_AMOUNT)
await connector.join(erc4626Adapter.address, tokenIn, JOIN_AMOUNT, 0)

const currentWethBalance = await weth.balanceOf(connector.address)
expect(currentWethBalance).to.be.equal(previousWethBalance.sub(JOIN_AMOUNT))
Expand Down Expand Up @@ -85,7 +89,7 @@ describe('ERC4626Connector', function () {

const exitShares = previousShares.div(2)
const exitAssets = previousAssets.div(2)
await connector.exit(erc4626Adapter.address, exitShares)
await connector.exit(erc4626Adapter.address, exitShares, 0)

const currentWethBalance = await weth.balanceOf(connector.address)
expect(currentWethBalance).to.be.equal(previousWethBalance.add(exitAssets))
Expand All @@ -99,7 +103,7 @@ describe('ERC4626Connector', function () {
const tokenIn = ONES_ADDRESS

it('reverts', async () => {
await expect(connector.join(erc4626Adapter.address, tokenIn, JOIN_AMOUNT)).to.be.revertedWith(
await expect(connector.join(erc4626Adapter.address, tokenIn, JOIN_AMOUNT, 0)).to.be.revertedWith(
'ERC4626InvalidToken'
)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ export function itBehavesLikeWormholeConnector(
})

context('when minAmountOut is greater than amount minus relayerFee', () => {
const minAmountOut = amount.add(1)
let minAmountOut: BigNumber

beforeEach('set min amount out', async () => {
minAmountOut = amount.add(1)
})

it('reverts', async function () {
await expect(
Expand Down

0 comments on commit 13dffe8

Please sign in to comment.