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 USDS-SUSDS token wrappers #13

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3fb0e1b
init
foodaka Sep 17, 2024
fb66cf8
fork execution
foodaka Sep 18, 2024
17ed1e5
cleanup
foodaka Sep 18, 2024
50f3eb7
test: Don't assume 0 ETH balance on wrapper contracts
CheyenneAtapour Sep 18, 2024
d3784d0
Fix TokenBorrow for SUSDS (#15)
CheyenneAtapour Sep 19, 2024
df5dd2b
feat: Move borrow logic to base wrapper
CheyenneAtapour Sep 20, 2024
0f69b1c
test: Add borrow test to SDAI wrapper. Lock blocknumbers
CheyenneAtapour Sep 20, 2024
e22cbd3
test: Add borrow token test for steth
CheyenneAtapour Sep 20, 2024
340414f
pr comments
foodaka Sep 24, 2024
a2dec9e
updated files
foodaka Sep 24, 2024
00b6ed4
move borrow as virtual on base
foodaka Sep 24, 2024
0163386
fix: Fuzz test, rename test, reorder fns per convention
CheyenneAtapour Sep 25, 2024
2cce3f7
borrow not permitted
foodaka Sep 25, 2024
0db3322
Merge branch 'feat/susds-wrappers' of github.com:aave/token-wrappers …
foodaka Sep 25, 2024
6915b96
borrow with sig
foodaka Sep 25, 2024
c24b358
fix: Move borrow logic to internal function
CheyenneAtapour Sep 25, 2024
1ab43a0
fix: Address pr comments
CheyenneAtapour Sep 26, 2024
4f5f382
fix: Move borrow logic to base wrapper
CheyenneAtapour Sep 27, 2024
1886062
fix: Move dependencies to V3 origin (#16)
miguelmtzinf Sep 30, 2024
9d654dd
fix: Remove unlimited allowance (#17)
miguelmtzinf Oct 1, 2024
253000b
feat: Generic ERC4626 <> ERC20 Token Wrapper (#20)
CheyenneAtapour Oct 3, 2024
53c52bd
fix: Remove unneccesary file
miguelmtzinf Oct 3, 2024
37e17c4
fix: Disables supply with permit for DAI because DAI permit is not st…
miguelmtzinf Oct 3, 2024
a10494c
fix: Re-order functions based on solidity guideliens
miguelmtzinf Oct 3, 2024
11c19c8
fix: Remove solc warnings
miguelmtzinf Oct 3, 2024
596d521
Refactors tests (#21)
miguelmtzinf Oct 3, 2024
86fb6e2
test: Add fuzz test for borrow
CheyenneAtapour Oct 4, 2024
9118be9
fix: Revert change test name
CheyenneAtapour Oct 4, 2024
13224ae
Add Additional BorrowToken Tests (#22)
CheyenneAtapour Oct 7, 2024
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
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/aave-v3-core"]
path = lib/aave-v3-core
url = https://github.com/aave/aave-v3-core
[submodule "lib/aave-address-book"]
path = lib/aave-address-book
url = https://github.com/bgd-labs/aave-address-book
2 changes: 2 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
src = "src"
out = "out"
libs = ["lib"]
solc = "0.8.24"
evm_version = "cancun"

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
2 changes: 1 addition & 1 deletion lib/aave-address-book
Submodule aave-address-book updated 298 files
1 change: 0 additions & 1 deletion lib/aave-v3-core
Submodule aave-v3-core deleted from 6070e8
8 changes: 8 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
aave-v3-core/=lib/aave-address-book/lib/aave-v3-origin/src/core/
aave-v3-periphery/=lib/aave-address-book/lib/aave-v3-origin/src/periphery/
aave-address-book/=lib/aave-address-book/src/
aave-v3-origin/=lib/aave-address-book/lib/aave-v3-origin/src/
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
solidity-utils/=lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/src/
openzeppelin/=lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts
73 changes: 68 additions & 5 deletions src/BaseTokenWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ pragma solidity ^0.8.10;
import {Ownable} from 'aave-v3-core/contracts/dependencies/openzeppelin/contracts/Ownable.sol';
import {IERC20} from 'aave-v3-core/contracts/dependencies/openzeppelin/contracts/IERC20.sol';
import {IERC20WithPermit} from 'aave-v3-core/contracts/interfaces/IERC20WithPermit.sol';
import {SafeERC20} from 'aave-v3-core/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol';
import {GPv2SafeERC20} from 'aave-v3-core/contracts/dependencies/gnosis/contracts/GPv2SafeERC20.sol';
import {IPool} from 'aave-v3-core/contracts/interfaces/IPool.sol';
import {IAToken} from 'aave-v3-core/contracts/interfaces/IAToken.sol';
import {ICreditDelegationToken} from 'aave-v3-core/contracts/interfaces/ICreditDelegationToken.sol';
import {IBaseTokenWrapper} from './interfaces/IBaseTokenWrapper.sol';

/**
Expand All @@ -26,6 +28,8 @@ abstract contract BaseTokenWrapper is Ownable, IBaseTokenWrapper {
/// @inheritdoc IBaseTokenWrapper
IPool public immutable POOL;

uint256 private constant VARIABLE_INTEREST_RATE_MODE = 2;

/**
* @dev Constructor
* @param tokenIn ERC-20 token that will be wrapped in supply operations
Expand All @@ -38,15 +42,14 @@ abstract contract BaseTokenWrapper is Ownable, IBaseTokenWrapper {
TOKEN_OUT = tokenOut;
POOL = IPool(pool);
transferOwnership(owner);
IERC20(tokenOut).approve(pool, type(uint256).max);
}

/// @inheritdoc IBaseTokenWrapper
function supplyToken(
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external returns (uint256) {
) external virtual returns (uint256) {
return _supplyToken(amount, onBehalfOf, referralCode);
}

Expand All @@ -56,7 +59,7 @@ abstract contract BaseTokenWrapper is Ownable, IBaseTokenWrapper {
address onBehalfOf,
uint16 referralCode,
PermitSignature calldata signature
) external returns (uint256) {
) external virtual returns (uint256) {
// explicitly left try-catch block blank to protect users from permit griefing
try
IERC20WithPermit(TOKEN_IN).permit(
Expand All @@ -76,7 +79,7 @@ abstract contract BaseTokenWrapper is Ownable, IBaseTokenWrapper {
function withdrawToken(
uint256 amount,
address to
) external returns (uint256) {
) external virtual returns (uint256) {
IAToken aTokenOut = IAToken(POOL.getReserveData(TOKEN_OUT).aTokenAddress);
return _withdrawToken(amount, to, aTokenOut);
}
Expand All @@ -86,7 +89,7 @@ abstract contract BaseTokenWrapper is Ownable, IBaseTokenWrapper {
uint256 amount,
address to,
PermitSignature calldata signature
) external returns (uint256) {
) external virtual returns (uint256) {
IAToken aTokenOut = IAToken(POOL.getReserveData(TOKEN_OUT).aTokenAddress);
// explicitly left try-catch block blank to protect users from permit griefing
try
Expand All @@ -103,6 +106,37 @@ abstract contract BaseTokenWrapper is Ownable, IBaseTokenWrapper {
return _withdrawToken(amount, to, aTokenOut);
}

/// @inheritdoc IBaseTokenWrapper
function borrowToken(uint256 amount, uint16 referralCode) external virtual {
_borrowToken(amount, msg.sender, referralCode);
}

/// @inheritdoc IBaseTokenWrapper
function borrowTokenWithPermit(
uint256 amount,
uint16 referralCode,
PermitSignature calldata signature
) external virtual {
if (signature.deadline != 0) {
address debtToken = POOL
.getReserveData(TOKEN_OUT)
.variableDebtTokenAddress;
// explicitly left try-catch block blank to protect users from permit griefing
try
ICreditDelegationToken(debtToken).delegationWithSig(
msg.sender,
address(this),
amount,
signature.deadline,
signature.v,
signature.r,
signature.s
)
{} catch {}
}
_borrowToken(amount, msg.sender, referralCode);
}

/// @inheritdoc IBaseTokenWrapper
function rescueTokens(
IERC20 token,
Expand Down Expand Up @@ -144,7 +178,9 @@ abstract contract BaseTokenWrapper is Ownable, IBaseTokenWrapper {
IERC20(TOKEN_IN).safeTransferFrom(msg.sender, address(this), amount);
uint256 amountWrapped = _wrapTokenIn(amount);
require(amountWrapped > 0, 'INSUFFICIENT_WRAPPED_TOKEN_RECEIVED');
SafeERC20.safeApprove(IERC20(TOKEN_OUT), address(POOL), amountWrapped);
POOL.supply(TOKEN_OUT, amountWrapped, onBehalfOf, referralCode);
SafeERC20.safeApprove(IERC20(TOKEN_OUT), address(POOL), 0);
return amountWrapped;
}

Expand Down Expand Up @@ -177,6 +213,33 @@ abstract contract BaseTokenWrapper is Ownable, IBaseTokenWrapper {
return amountUnwrapped;
}

/**
* @notice Helper to borrow token from the Pool and unwraps it, sending to the recipient
* @param amount The amount of token to borrow
* @param onBehalfOf The address that will receive the unwrapped token
* @param referralCode Code used to register the integrator originating the operation, for potential rewards
*/
function _borrowToken(
uint256 amount,
address onBehalfOf,
uint16 referralCode
) internal {
require(amount > 0, 'INSUFFICIENT_AMOUNT_TO_BORROW');
uint256 balanceBeforeBorrow = IERC20(TOKEN_OUT).balanceOf(address(this));
POOL.borrow(
TOKEN_OUT,
amount,
VARIABLE_INTEREST_RATE_MODE,
referralCode,
address(onBehalfOf)
);
uint256 balanceAfterBorrow = IERC20(TOKEN_OUT).balanceOf(address(this));
uint256 amountIn = _unwrapTokenOut(
balanceAfterBorrow - balanceBeforeBorrow
);
IERC20(TOKEN_IN).transfer(onBehalfOf, amountIn);
}

/**
* @notice Helper to wrap an amount of tokenIn, receiving tokenOut
* @param amount The amount of tokenIn to wrap
Expand Down
57 changes: 57 additions & 0 deletions src/Generic4626Wrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.10;

import {SafeERC20} from 'aave-v3-core/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol';
import {IERC20} from 'aave-v3-core/contracts/dependencies/openzeppelin/contracts/IERC20.sol';
import {IERC4626} from 'openzeppelin/interfaces/IERC4626.sol';
import {BaseTokenWrapper} from './BaseTokenWrapper.sol';

/**
* @title Generic4626Wrapper
* @author Aave
* @notice Generic contract to wrap an ERC20 to ERC4626 to on supply to Aave, or unwrap from ERC4626 to ERC20 on withdrawal
*/
contract Generic4626Wrapper is BaseTokenWrapper {
/**
* @dev Constructor
* @param tokenIn Address for the ERC20 token
* @param tokenOut Address for the ERC4626 token
* @param pool The address of the Aave Pool
* @param owner The address to transfer ownership to
*/
constructor(
address tokenIn,
address tokenOut,
address pool,
address owner
) BaseTokenWrapper(tokenIn, tokenOut, pool, owner) {
// Intentionally left blank
}

/// @inheritdoc BaseTokenWrapper
function getTokenOutForTokenIn(
uint256 amount
) external view override returns (uint256) {
return IERC4626(TOKEN_OUT).previewDeposit(amount);
}

/// @inheritdoc BaseTokenWrapper
function getTokenInForTokenOut(
uint256 amount
) external view override returns (uint256) {
return IERC4626(TOKEN_OUT).previewRedeem(amount);
}

/// @inheritdoc BaseTokenWrapper
function _wrapTokenIn(uint256 amount) internal override returns (uint256) {
SafeERC20.safeApprove(IERC20(TOKEN_IN), TOKEN_OUT, amount);
uint256 wrappedAmount = IERC4626(TOKEN_OUT).deposit(amount, address(this));
SafeERC20.safeApprove(IERC20(TOKEN_IN), TOKEN_OUT, 0);
return wrappedAmount;
}

/// @inheritdoc BaseTokenWrapper
function _unwrapTokenOut(uint256 amount) internal override returns (uint256) {
return IERC4626(TOKEN_OUT).redeem(amount, address(this), address(this));
}
}
37 changes: 34 additions & 3 deletions src/SavingsDaiTokenWrapper.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.10;

import {SafeERC20} from 'aave-v3-core/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol';
import {IERC20} from 'aave-v3-core/contracts/dependencies/openzeppelin/contracts/IERC20.sol';
import {ISavingsDai} from './interfaces/ISavingsDai.sol';
import {ISavingsDai} from './dependencies/ISavingsDai.sol';
import {BaseTokenWrapper} from './BaseTokenWrapper.sol';

/**
Expand All @@ -24,7 +25,31 @@ contract SavingsDaiTokenWrapper is BaseTokenWrapper {
address pool,
address owner
) BaseTokenWrapper(tokenIn, tokenOut, pool, owner) {
IERC20(tokenIn).approve(tokenOut, type(uint256).max);
// Intentionally left blank
}

/// @inheritdoc BaseTokenWrapper
function supplyTokenWithPermit(
uint256,
address,
uint16,
PermitSignature calldata
) external pure override returns (uint256) {
revert('INVALID_ACTION');
}

/// @inheritdoc BaseTokenWrapper
function borrowToken(uint256, uint16) external pure override {
revert('INVALID_ACTION');
}

/// @inheritdoc BaseTokenWrapper
function borrowTokenWithPermit(
uint256,
uint16,
PermitSignature calldata
) external pure override {
revert('INVALID_ACTION');
}

/// @inheritdoc BaseTokenWrapper
Expand All @@ -43,7 +68,13 @@ contract SavingsDaiTokenWrapper is BaseTokenWrapper {

/// @inheritdoc BaseTokenWrapper
function _wrapTokenIn(uint256 amount) internal override returns (uint256) {
return ISavingsDai(TOKEN_OUT).deposit(amount, address(this));
SafeERC20.safeApprove(IERC20(TOKEN_IN), TOKEN_OUT, amount);
uint256 wrappedAmount = ISavingsDai(TOKEN_OUT).deposit(
amount,
address(this)
);
SafeERC20.safeApprove(IERC20(TOKEN_IN), TOKEN_OUT, 0);
return wrappedAmount;
}

/// @inheritdoc BaseTokenWrapper
Expand Down
24 changes: 21 additions & 3 deletions src/StakedEthTokenWrapper.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.10;

import {SafeERC20} from 'aave-v3-core/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol';
import {IERC20} from 'aave-v3-core/contracts/dependencies/openzeppelin/contracts/IERC20.sol';
import {IWstETH} from './interfaces/IWstETH.sol';
import {IWstETH} from './dependencies/IWstETH.sol';
import {BaseTokenWrapper} from './BaseTokenWrapper.sol';

/**
Expand All @@ -24,7 +25,21 @@ contract StakedEthTokenWrapper is BaseTokenWrapper {
address pool,
address owner
) BaseTokenWrapper(tokenIn, tokenOut, pool, owner) {
IERC20(tokenIn).approve(tokenOut, type(uint256).max);
// Intentionally left blank
}

/// @inheritdoc BaseTokenWrapper
function borrowToken(uint256, uint16) external pure override {
revert('INVALID_ACTION');
}

/// @inheritdoc BaseTokenWrapper
function borrowTokenWithPermit(
uint256,
uint16,
PermitSignature calldata
) external pure override {
revert('INVALID_ACTION');
}

/// @inheritdoc BaseTokenWrapper
Expand All @@ -43,7 +58,10 @@ contract StakedEthTokenWrapper is BaseTokenWrapper {

/// @inheritdoc BaseTokenWrapper
function _wrapTokenIn(uint256 amount) internal override returns (uint256) {
return IWstETH(TOKEN_OUT).wrap(amount);
SafeERC20.safeApprove(IERC20(TOKEN_IN), TOKEN_OUT, amount);
uint256 wrappedAmount = IWstETH(TOKEN_OUT).wrap(amount);
SafeERC20.safeApprove(IERC20(TOKEN_IN), TOKEN_OUT, 0);
return wrappedAmount;
}

/// @inheritdoc BaseTokenWrapper
Expand Down
File renamed without changes.
Loading