Skip to content
Closed
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
3 changes: 3 additions & 0 deletions src/BaseTokenWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ abstract contract BaseTokenWrapper is Ownable, IBaseTokenWrapper {
return _withdrawToken(amount, to, aTokenOut);
}

/// @inheritdoc IBaseTokenWrapper
function borrowToken(uint256 amount, address to) external virtual {}

/// @inheritdoc IBaseTokenWrapper
function rescueTokens(
IERC20 token,
Expand Down
7 changes: 7 additions & 0 deletions src/SavingsSuSDSTokenWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ contract SavingsSuSDSTokenWrapper is BaseTokenWrapper {
IERC20(tokenIn).approve(tokenOut, type(uint256).max);
}

///@inheritdoc BaseTokenWrapper
function borrowToken(uint256 amount, address to) external override {
POOL.borrow(TOKEN_OUT, amount, 1, 0, address(this));
uint256 amountIn = _unwrapTokenOut(amount);
IERC20(TOKEN_IN).transfer(to, amountIn);
}

/// @inheritdoc BaseTokenWrapper
function getTokenOutForTokenIn(
uint256 amount
Expand Down
7 changes: 7 additions & 0 deletions src/interfaces/IBaseTokenWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ interface IBaseTokenWrapper {
PermitSignature calldata signature
) external returns (uint256);

/**
* @notice Borrows token from the Pool and unwraps it, sending to the recipient
* @param amount The amount of token to borrow
* @param to The address that will receive the unwrapped token
*/
function borrowToken(uint256 amount, address to) external;

/**
* @notice Provides way for the contract owner to rescue ERC-20 tokens
* @param token The address of the token to withdraw from this contract
Expand Down
49 changes: 49 additions & 0 deletions src/interfaces/ICreditDelegationToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
* @title ICreditDelegationToken
* @author Aave
* @notice Defines the basic interface for a token supporting credit delegation.
**/
interface ICreditDelegationToken {
/**
* @notice Delegates borrowing power to a user on the specific debt token.
* Delegation will still respect the liquidation constraints (even if delegated, a
* delegatee cannot force a delegator HF to go below 1)
* @param delegatee The address receiving the delegated borrowing power
* @param amount The maximum amount being delegated.
**/
function approveDelegation(address delegatee, uint256 amount) external;

/**
* @notice Returns the borrow allowance of the user
* @param fromUser The user to giving allowance
* @param toUser The user to give allowance to
* @return The current allowance of `toUser`
**/
function borrowAllowance(
address fromUser,
address toUser
) external view returns (uint256);

/**
* @notice Delegates borrowing power to a user on the specific debt token via ERC712 signature
* @param delegator The delegator of the credit
* @param delegatee The delegatee that can use the credit
* @param value The amount to be delegated
* @param deadline The deadline timestamp, type(uint256).max for max deadline
* @param v The V signature param
* @param s The S signature param
* @param r The R signature param
*/
function delegationWithSig(
address delegator,
address delegatee,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
47 changes: 47 additions & 0 deletions test/SavingsSuSDSTokenWrapper.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ pragma solidity ^0.8.10;
import 'forge-std/console2.sol';

import {IERC20} from 'aave-v3-core/contracts/dependencies/openzeppelin/contracts/IERC20.sol';
import {IPool} from 'aave-v3-core/contracts/interfaces/IPool.sol';
import {BaseTokenWrapperTest} from './BaseTokenWrapper.t.sol';
import {SavingsSuSDSTokenWrapper} from '../src/SavingsSuSDSTokenWrapper.sol';
import {ICreditDelegationToken} from '../src/interfaces/ICreditDelegationToken.sol';

// frontend deposits usds and automatically converted to susds on aave
contract SavingsSuSDSTokenWrapperTest is BaseTokenWrapperTest {
address constant USDS = 0xdC035D45d973E3EC169d2276DDab16f1e407384F;
address constant SUSDS = 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD;
address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

// TODO Actual Address --> fork
address constant AUSDS = 0x5c647cE0Ae10658ec44FA4E11A51c96e94efd1Dd;
Expand Down Expand Up @@ -49,4 +52,48 @@ contract SavingsSuSDSTokenWrapperTest is BaseTokenWrapperTest {
'Unexpected TOKEN_IN allowance'
);
}

function testBorrow() public {
address alice = makeAddr('ALICE');
deal(USDS, alice, 1000e18);
vm.startPrank(alice);
IERC20(USDS).approve(address(pool), 1000e18);
IPool(pool).supply(USDS, 1000e18, alice, 0);
vm.stopPrank();

// TODO: Instead of checking pool, need to check reserve contract
/*
assertEq(
IERC20(SUSDS).balanceOf(address(pool)),
1e18,
'Unexpected post-deal pool USDS balance'
);*/

deal(WETH, address(this), 20 ether);
IERC20(WETH).approve(pool, 20 ether);
IPool(pool).supply(WETH, 20 ether, address(this), 0);

deal(USDS, address(this), 1e18);
IERC20(USDS).approve(address(pool), 1e18);
IPool(pool).supply(USDS, 1e18, address(this), 0);
deal(SUSDS, address(this), 1e18);
IERC20(SUSDS).approve(address(pool), 1e18);
IPool(pool).supply(SUSDS, 1e18, address(this), 0);

uint256 amount = 1e18;
uint256 amountOut = tokenWrapper.getTokenOutForTokenIn(amount);
uint256 usdsBefore = IERC20(USDS).balanceOf(address(this));
ICreditDelegationToken(SUSDS).approveDelegation(
address(tokenWrapper),
amountOut
);
tokenWrapper.borrowToken(amount, address(this));
uint256 usdsAfter = IERC20(USDS).balanceOf(address(this));

assertEq(
usdsAfter,
usdsBefore + amount,
'Unexpected USDS balance after borrow'
);
}
}