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

Fix TokenBorrow for SUSDS #15

Merged
merged 5 commits into from
Sep 19, 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
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
11 changes: 11 additions & 0 deletions src/SavingsSuSDSTokenWrapper.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.10;
import 'forge-std/console2.sol';

import {IERC20} from 'aave-v3-core/contracts/dependencies/openzeppelin/contracts/IERC20.sol';
import {IUSDS} from './interfaces/IUSDS.sol';
Expand Down Expand Up @@ -27,6 +28,16 @@ contract SavingsSuSDSTokenWrapper is BaseTokenWrapper {
IERC20(tokenIn).approve(tokenOut, type(uint256).max);
}

///@inheritdoc BaseTokenWrapper
function borrowToken(uint256 amount, address to) external override {
require(amount > 0, 'INSUFFICIENT_AMOUNT_TO_BORROW');

POOL.borrow(TOKEN_OUT, amount, 2, 0, address(to));

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;
}
83 changes: 81 additions & 2 deletions test/SavingsSuSDSTokenWrapper.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@
pragma solidity ^0.8.10;
import 'forge-std/console2.sol';

import {DataTypes} from 'aave-v3-core/contracts/protocol/libraries/types/DataTypes.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;
address constant AUSDS = 0x10Ac93971cdb1F5c778144084242374473c350Da;

function setUp() public {
// vm.createSelectFork(vm.envString('ETH_RPC_URL'));
vm.createSelectFork(
'https://rpc.tenderly.co/fork/26fdbc41-5ae7-4f5a-9b47-a4ae15e05ce0'
'https://rpc.tenderly.co/fork/881012fd-267f-41dc-93ba-8eb025b8bce2'
);
pool = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2;

Expand Down Expand Up @@ -49,4 +53,79 @@ 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'
// );
// }

function testBorrow() public {
address debtToken = IPool(pool)
.getReserveData(SUSDS)
.variableDebtTokenAddress;

address alice = makeAddr('ALICE');
uint256 collateralAmount = 1000e18;

uint256 borrowAmount = 100e18;

deal(WETH, alice, collateralAmount);

vm.startPrank(alice);
IERC20(WETH).approve(address(pool), collateralAmount);
IPool(pool).supply(WETH, collateralAmount, alice, 0);

ICreditDelegationToken(debtToken).approveDelegation(
address(tokenWrapper),
borrowAmount
);

// IPool(pool).borrow(SUSDS, borrowAmount, 2, 0, alice);
// console2.log('Borrowing USDS', address(tokenWrapper));
tokenWrapper.borrowToken(borrowAmount, address(alice));

vm.stopPrank();

uint256 borrowedAmount = tokenWrapper.getTokenInForTokenOut(borrowAmount);
assertEq(IERC20(USDS).balanceOf(address(alice)), borrowedAmount);
}
}