-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: adapters for USDS and SKY rewards * feat: integrations tests + zircuit fix * fix: add `earned` to `IStakingRewards`
- Loading branch information
Showing
23 changed files
with
1,299 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { | ||
PoolV3CoreConfigurator, | ||
testDaiConfigMainnet, | ||
} from "@gearbox-protocol/sdk-gov"; | ||
|
||
const poolCfg = PoolV3CoreConfigurator.new(testDaiConfigMainnet); | ||
console.error(poolCfg.toString()); | ||
|
||
console.log(poolCfg.deployConfig()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Foundation, 2024. | ||
pragma solidity ^0.8.23; | ||
|
||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
|
||
import {ICreditManagerV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditManagerV3.sol"; | ||
|
||
import {AbstractAdapter} from "../AbstractAdapter.sol"; | ||
|
||
import {IDaiUsds} from "../../integrations/sky/IDaiUsds.sol"; | ||
import {IDaiUsdsAdapter} from "../../interfaces/sky/IDaiUsdsAdapter.sol"; | ||
|
||
/// @title DaiUsds Adapter | ||
/// @notice Implements logic for interacting with the DAI / USDS wrapping contract | ||
contract DaiUsdsAdapter is AbstractAdapter, IDaiUsdsAdapter { | ||
bytes32 public constant override contractType = "AD_DAI_USDS_EXCHANGE"; | ||
uint256 public constant override version = 3_10; | ||
|
||
/// @notice DAI token | ||
address public immutable override dai; | ||
|
||
/// @notice USDS token | ||
address public immutable override usds; | ||
|
||
/// @notice Constructor | ||
/// @param _creditManager Credit manager address | ||
/// @param _targetContract DAI / USDS exchange contract | ||
constructor(address _creditManager, address _targetContract) AbstractAdapter(_creditManager, _targetContract) { | ||
dai = IDaiUsds(_targetContract).dai(); | ||
usds = IDaiUsds(_targetContract).usds(); | ||
|
||
// We check that DAI and USDS are both valid collaterals | ||
_getMaskOrRevert(dai); | ||
_getMaskOrRevert(usds); | ||
} | ||
|
||
/// @notice Swaps given amount of DAI to USDS | ||
/// @param wad Amount of DAI to swap | ||
/// @dev `usr` (recipient) is ignored as it is always the Credit Account | ||
function daiToUsds(address, uint256 wad) external override creditFacadeOnly returns (bool) { | ||
address creditAccount = _creditAccount(); | ||
_daiToUsds(creditAccount, wad); | ||
return false; | ||
} | ||
|
||
/// @notice Swaps the entire balance of DAI to USDS, except the specified amount | ||
/// @param leftoverAmount Amount of DAI to keep on the account | ||
function daiToUsdsDiff(uint256 leftoverAmount) external override creditFacadeOnly returns (bool) { | ||
address creditAccount = _creditAccount(); | ||
|
||
uint256 balance = IERC20(dai).balanceOf(creditAccount); | ||
if (balance > leftoverAmount) { | ||
unchecked { | ||
_daiToUsds(creditAccount, balance - leftoverAmount); | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
/// @dev Internal implementation for `daiToUsds` and `daiToUsdsDiff` | ||
function _daiToUsds(address creditAccount, uint256 amount) internal { | ||
_executeSwapSafeApprove(dai, abi.encodeCall(IDaiUsds.daiToUsds, (creditAccount, amount))); | ||
} | ||
|
||
/// @notice Swaps given amount of USDS to DAI | ||
/// @param wad Amount of USDS to swap | ||
/// @dev `usr` (recipient) is ignored as it is always the Credit Account | ||
function usdsToDai(address, uint256 wad) external override creditFacadeOnly returns (bool) { | ||
address creditAccount = _creditAccount(); | ||
_usdsToDai(creditAccount, wad); | ||
return false; | ||
} | ||
|
||
/// @notice Swaps the entire balance of USDS to DAI, except the specified amount | ||
/// @param leftoverAmount Amount of USDS to keep on the account | ||
function usdsToDaiDiff(uint256 leftoverAmount) external override creditFacadeOnly returns (bool) { | ||
address creditAccount = _creditAccount(); | ||
|
||
uint256 balance = IERC20(usds).balanceOf(creditAccount); | ||
if (balance > leftoverAmount) { | ||
unchecked { | ||
_usdsToDai(creditAccount, balance - leftoverAmount); | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
/// @dev Internal implementation for `usdsToDai` and `usdsToDaiDiff` | ||
function _usdsToDai(address creditAccount, uint256 amount) internal { | ||
_executeSwapSafeApprove(usds, abi.encodeCall(IDaiUsds.usdsToDai, (creditAccount, amount))); | ||
} | ||
|
||
/// @notice Serialized adapter parameters | ||
function serialize() external view returns (bytes memory serializedData) { | ||
serializedData = abi.encode(creditManager, targetContract, dai, usds); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Foundation, 2024. | ||
pragma solidity ^0.8.23; | ||
|
||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
|
||
import {ICreditManagerV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditManagerV3.sol"; | ||
import {AbstractAdapter} from "../AbstractAdapter.sol"; | ||
import {BitMask} from "@gearbox-protocol/core-v3/contracts/libraries/BitMask.sol"; | ||
|
||
import {IStakingRewards} from "../../integrations/sky/IStakingRewards.sol"; | ||
import {IStakingRewardsAdapter} from "../../interfaces/sky/IStakingRewardsAdapter.sol"; | ||
|
||
/// @title Staking Rewards adapter | ||
/// @notice Implements logic for interacting with a generic StakingRewards contract | ||
contract StakingRewardsAdapter is AbstractAdapter, IStakingRewardsAdapter { | ||
using BitMask for uint256; | ||
|
||
bytes32 public constant override contractType = "AD_STAKING_REWARDS"; | ||
uint256 public constant override version = 3_10; | ||
|
||
/// @notice Address of the staking token | ||
address public immutable override stakingToken; | ||
|
||
/// @notice Address of the rewards token | ||
address public immutable override rewardsToken; | ||
|
||
/// @notice Address of a phantom token representing account's stake in the reward pool | ||
address public immutable override stakedPhantomToken; | ||
|
||
/// @notice Constructor | ||
/// @param _creditManager Credit manager address | ||
/// @param _stakingRewards StakingRewards contract address | ||
/// @param _stakedPhantomToken Staked phantom token address | ||
constructor(address _creditManager, address _stakingRewards, address _stakedPhantomToken) | ||
AbstractAdapter(_creditManager, _stakingRewards) | ||
{ | ||
stakingToken = IStakingRewards(_stakingRewards).stakingToken(); | ||
_getMaskOrRevert(stakingToken); | ||
|
||
rewardsToken = IStakingRewards(_stakingRewards).rewardsToken(); | ||
_getMaskOrRevert(rewardsToken); | ||
|
||
stakedPhantomToken = _stakedPhantomToken; | ||
_getMaskOrRevert(stakedPhantomToken); | ||
} | ||
|
||
// ----- // | ||
// STAKE // | ||
// ----- // | ||
|
||
/// @notice Stakes tokens in the StakingRewards contract | ||
/// @param amount Amount of tokens to stake | ||
function stake(uint256 amount) external override creditFacadeOnly returns (bool) { | ||
_executeSwapSafeApprove(stakingToken, abi.encodeCall(IStakingRewards.stake, (amount))); | ||
return false; | ||
} | ||
|
||
/// @notice Stakes the entire balance of staking token, except the specified amount | ||
/// @param leftoverAmount Amount of staking token to keep on the account | ||
function stakeDiff(uint256 leftoverAmount) external override creditFacadeOnly returns (bool) { | ||
address creditAccount = _creditAccount(); | ||
|
||
uint256 balance = IERC20(stakingToken).balanceOf(creditAccount); | ||
|
||
if (balance > leftoverAmount) { | ||
unchecked { | ||
_executeSwapSafeApprove(stakingToken, abi.encodeCall(IStakingRewards.stake, (balance - leftoverAmount))); | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
// ----- // | ||
// CLAIM // | ||
// ----- // | ||
|
||
/// @notice Claims rewards on the current position | ||
function getReward() external override creditFacadeOnly returns (bool) { | ||
_execute(abi.encodeCall(IStakingRewards.getReward, ())); | ||
return false; | ||
} | ||
|
||
// -------- // | ||
// WITHDRAW // | ||
// -------- // | ||
|
||
/// @notice Withdraws staked tokens from the StakingRewards contract | ||
/// @param amount Amount of tokens to withdraw | ||
function withdraw(uint256 amount) external override creditFacadeOnly returns (bool) { | ||
_execute(abi.encodeCall(IStakingRewards.withdraw, (amount))); | ||
return false; | ||
} | ||
|
||
/// @notice Withdraws the entire balance of staked tokens, except the specified amount | ||
/// @param leftoverAmount Amount of staked tokens to keep in the contract | ||
function withdrawDiff(uint256 leftoverAmount) external override creditFacadeOnly returns (bool) { | ||
address creditAccount = _creditAccount(); | ||
|
||
uint256 balance = IERC20(stakedPhantomToken).balanceOf(creditAccount); | ||
|
||
if (balance > leftoverAmount) { | ||
unchecked { | ||
_execute(abi.encodeCall(IStakingRewards.withdraw, (balance - leftoverAmount))); | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/// @notice Withdraws phantom token for its underlying | ||
/// @dev `token` parameter is ignored as adapter only handles one token | ||
function withdrawPhantomToken(address, uint256 amount) external override creditFacadeOnly returns (bool) { | ||
_execute(abi.encodeCall(IStakingRewards.withdraw, (amount))); | ||
return false; | ||
} | ||
|
||
/// @notice Serialized adapter parameters | ||
function serialize() external view returns (bytes memory serializedData) { | ||
serializedData = abi.encode(creditManager, targetContract, stakingToken, rewardsToken, stakedPhantomToken); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Foundation, 2024. | ||
pragma solidity ^0.8.23; | ||
|
||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import {IStakingRewards} from "../../integrations/sky/IStakingRewards.sol"; | ||
import {PhantomERC20} from "../PhantomERC20.sol"; | ||
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; | ||
import {MultiCall} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditFacadeV3.sol"; | ||
import {IPhantomToken} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IPhantomToken.sol"; | ||
|
||
/// @title StakingRewards position token | ||
/// @notice Phantom ERC-20 token that represents the balance of the staked position in a StakingRewards pool | ||
contract StakingRewardsPhantomToken is PhantomERC20, IPhantomToken { | ||
uint256 public constant override version = 3_10; | ||
bytes32 public constant override contractType = "PT_STAKING_REWARDS"; | ||
|
||
address public immutable pool; | ||
|
||
/// @notice Constructor | ||
/// @param _pool The rewards pool where the balance is tracked | ||
constructor(address _pool) | ||
PhantomERC20( | ||
IStakingRewards(_pool).stakingToken(), | ||
string( | ||
abi.encodePacked( | ||
"StakingRewards staked position ", IERC20Metadata(IStakingRewards(_pool).stakingToken()).name() | ||
) | ||
), | ||
string(abi.encodePacked("stk", IERC20Metadata(IStakingRewards(_pool).stakingToken()).symbol())), | ||
IERC20Metadata(IStakingRewards(_pool).stakingToken()).decimals() | ||
) | ||
{ | ||
pool = _pool; | ||
} | ||
|
||
/// @notice Returns the amount of underlying tokens staked in the pool | ||
/// @param account The account for which the calculation is performed | ||
function balanceOf(address account) public view returns (uint256) { | ||
return IERC20(pool).balanceOf(account); | ||
} | ||
|
||
/// @notice Returns phantom token's target contract and underlying | ||
function getPhantomTokenInfo() external view override returns (address, address) { | ||
return (pool, underlying); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.23; | ||
|
||
interface IDaiUsds { | ||
function daiToUsds(address usr, uint256 wad) external; | ||
|
||
function usdsToDai(address usr, uint256 wad) external; | ||
|
||
function dai() external view returns (address); | ||
|
||
function usds() external view returns (address); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.23; | ||
|
||
/// @title Sky Staking Rewards Interface | ||
/// @notice Interface for the Sky StakingRewards contract | ||
interface IStakingRewards { | ||
function rewardsToken() external view returns (address); | ||
function stakingToken() external view returns (address); | ||
|
||
function stake(uint256 amount) external; | ||
function withdraw(uint256 amount) external; | ||
function getReward() external; | ||
function earned(address account) external view returns (uint256); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// SPDX-License-Identifier: MIT | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Foundation, 2024. | ||
pragma solidity ^0.8.23; | ||
|
||
import {IAdapter} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IAdapter.sol"; | ||
|
||
/// @title DaiUsds adapter interface | ||
interface IDaiUsdsAdapter is IAdapter { | ||
function dai() external view returns (address); | ||
|
||
function usds() external view returns (address); | ||
|
||
function daiToUsds(address, uint256) external returns (bool); | ||
|
||
function usdsToDai(address, uint256) external returns (bool); | ||
|
||
function daiToUsdsDiff(uint256) external returns (bool); | ||
|
||
function usdsToDaiDiff(uint256) external returns (bool); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// SPDX-License-Identifier: MIT | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Foundation, 2024. | ||
pragma solidity ^0.8.23; | ||
|
||
import {IAdapter} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IAdapter.sol"; | ||
import {IPhantomTokenWithdrawer} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IPhantomToken.sol"; | ||
|
||
/// @title Staking Rewards Adapter Interface | ||
interface IStakingRewardsAdapter is IAdapter, IPhantomTokenWithdrawer { | ||
function stakingToken() external view returns (address); | ||
|
||
function rewardsToken() external view returns (address); | ||
|
||
function stakedPhantomToken() external view returns (address); | ||
|
||
function stake(uint256 amount) external returns (bool useSafePrices); | ||
|
||
function stakeDiff(uint256 leftoverAmount) external returns (bool useSafePrices); | ||
|
||
function getReward() external returns (bool useSafePrices); | ||
|
||
function withdraw(uint256 amount) external returns (bool useSafePrices); | ||
|
||
function withdrawDiff(uint256 leftoverAmount) external returns (bool useSafePrices); | ||
} |
Oops, something went wrong.