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

auth router #90

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
173 changes: 173 additions & 0 deletions src/TurboRouterAuth.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.10;

import {TurboMaster} from "./TurboMaster.sol";
import {TurboSafe} from "./TurboSafe.sol";

import {ENSReverseRecordAuth} from "./ens/ENSReverseRecordAuth.sol";
import {IERC4626, ERC4626RouterBase, IWETH9, PeripheryPayments} from "ERC4626/ERC4626RouterBase.sol";

import {ERC20} from "solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";

import {ERC4626} from "solmate/mixins/ERC4626.sol";
import {Auth, Authority} from "solmate/auth/Auth.sol";

/**
@title a router which can perform multiple Turbo actions between Master and the Safes
@notice routes custom users flows between actions on the master and safes.

Extends the ERC4626RouterBase to allow for flexible combinations of actions involving ERC4626 and permit, weth, and Turbo specific actions.

Safe Creation has functions bundled with deposit (and optionally boost) because a newly created Safe address can only be known at runtime.
The caller is always atomically given the owner role of a new safe.

Authentication requires the caller to be the owner of the Safe to perform any ERC4626 method or TurboSafe requiresAuth method.
Assumes the Safe's authority gives permission to call these functions to the TurboRouter.
*/
contract TurboRouterAuth is ERC4626RouterBase, ENSReverseRecordAuth {
using SafeTransferLib for ERC20;

TurboMaster public immutable master;

constructor (TurboMaster _master, address _owner, Authority _authority, IWETH9 weth) Auth(_owner, _authority) PeripheryPayments(weth) {
master = _master;
}

modifier authenticate(address target) {
require(msg.sender == Auth(target).owner() || Auth(target).authority().canCall(msg.sender, target, msg.sig), "NOT_AUTHED");

_;
}

function createSafe(ERC20 underlying) external requiresAuth returns (TurboSafe safe) {
(safe, ) = master.createSafe(underlying);

safe.setOwner(msg.sender);
}

function createSafeAndDeposit(ERC20 underlying, address to, uint256 amount, uint256 minSharesOut) external requiresAuth returns (TurboSafe safe) {
(safe, ) = master.createSafe(underlying);

// approve max from router to save depositor gas in future.
approve(underlying, address(safe), type(uint256).max);

super.deposit(IERC4626(address(safe)), to, amount, minSharesOut);

safe.setOwner(msg.sender);
}

function createSafeAndDepositAndBoost(
ERC20 underlying,
address to,
uint256 amount,
uint256 minSharesOut,
ERC4626 boostedVault,
uint256 boostedFeiAmount
) public requiresAuth returns (TurboSafe safe) {
(safe, ) = master.createSafe(underlying);

// approve max from router to save depositor gas in future.
approve(underlying, address(safe), type(uint256).max);

super.deposit(IERC4626(address(safe)), to, amount, minSharesOut);

safe.boost(boostedVault, boostedFeiAmount);

safe.setOwner(msg.sender);
}

function createSafeAndDepositAndBoostMany(
ERC20 underlying,
address to,
uint256 amount,
uint256 minSharesOut,
ERC4626[] calldata boostedVaults,
uint256[] calldata boostedFeiAmounts
) public requiresAuth returns (TurboSafe safe) {
(safe, ) = master.createSafe(underlying);

// approve max from router to save depositor gas in future.
approve(underlying, address(safe), type(uint256).max);

super.deposit(IERC4626(address(safe)), to, amount, minSharesOut);

unchecked {
require(boostedVaults.length == boostedFeiAmounts.length, "length");
for (uint256 i = 0; i < boostedVaults.length; i++) {
safe.boost(boostedVaults[i], boostedFeiAmounts[i]);
}
}

safe.setOwner(msg.sender);
}

function deposit(IERC4626 safe, address to, uint256 amount, uint256 minSharesOut)
public
payable
override
authenticate(address(safe))
returns (uint256)
{
return super.deposit(safe, to, amount, minSharesOut);
}

function mint(IERC4626 safe, address to, uint256 shares, uint256 maxAmountIn)
public
payable
override
authenticate(address(safe))
returns (uint256)
{
return super.mint(safe, to, shares, maxAmountIn);
}

function withdraw(IERC4626 safe, address to, uint256 amount, uint256 maxSharesOut)
public
payable
override
authenticate(address(safe))
returns (uint256)
{
return super.withdraw(safe, to, amount, maxSharesOut);
}

function redeem(IERC4626 safe, address to, uint256 shares, uint256 minAmountOut)
public
payable
override
authenticate(address(safe))
returns (uint256)
{
return super.redeem(safe, to, shares, minAmountOut);
}

function slurp(TurboSafe safe, ERC4626 vault) external authenticate(address(safe)) {
safe.slurp(vault);
}

function boost(TurboSafe safe, ERC4626 vault, uint256 feiAmount) public authenticate(address(safe)) {
safe.boost(vault, feiAmount);
}

function less(TurboSafe safe, ERC4626 vault, uint256 feiAmount) external authenticate(address(safe)) {
safe.less(vault, feiAmount);
}

function lessAll(TurboSafe safe, ERC4626 vault) external authenticate(address(safe)) {
safe.less(vault, vault.maxWithdraw(address(safe)));
}

function sweep(TurboSafe safe, address to, ERC20 token, uint256 amount) external authenticate(address(safe)) {
safe.sweep(to, token, amount);
}

function sweepAll(TurboSafe safe, address to, ERC20 token) external authenticate(address(safe)) {
safe.sweep(to, token, token.balanceOf(address(safe)));
}

function slurpAndLessAll(TurboSafe safe, ERC4626 vault) external authenticate(address(safe)) {
safe.slurp(vault);
safe.less(vault, vault.maxWithdraw(address(safe)));
}
}
11 changes: 11 additions & 0 deletions src/deploy/Configurer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ contract Configurer {
/// @notice limited version of TURBO_ADMIN_ROLE which can manage collateral and vault parameters.
uint8 public constant TURBO_STRATEGIST_ROLE = 7;

uint8 public constant CREATE_SAFE_ROLE = 8;

// TODO: Deploy TurboRouterAuth with existing Turbo Authority. Create safe role gets these powers. Grant role to Balancer + Olympus + whoever wants to test

/******************** CONFIGURATION ********************/

/// @notice configure the turbo timelock. Requires TIMELOCK_ADMIN_ROLE over timelock.
Expand All @@ -74,6 +78,13 @@ contract Configurer {

/// @notice configure the turbo authority. Requires ownership over turbo authority.
function configureAuthority(MultiRolesAuthority turboAuthority) public {
// CREATE_SAFE_ROLE
turboAuthority.setRoleCapability(CREATE_SAFE_ROLE, TurboMaster.createSafe.selector, true);
turboAuthority.setRoleCapability(CREATE_SAFE_ROLE, TurboRouter.createSafe.selector, true);
turboAuthority.setRoleCapability(CREATE_SAFE_ROLE, TurboRouter.createSafeAndDeposit.selector, true);
turboAuthority.setRoleCapability(CREATE_SAFE_ROLE, TurboRouter.createSafeAndDepositAndBoost.selector, true);
turboAuthority.setRoleCapability(CREATE_SAFE_ROLE, TurboRouter.createSafeAndDepositAndBoostMany.selector, true);

// GIBBER_ROLE
turboAuthority.setRoleCapability(GIBBER_ROLE, TurboSafe.gib.selector, true);

Expand Down