Skip to content
This repository has been archived by the owner on Oct 6, 2023. It is now read-only.

Commit

Permalink
initial commit, mostly copy-pasting to get started
Browse files Browse the repository at this point in the history
  • Loading branch information
stevieraykatz committed Oct 2, 2023
1 parent 0240014 commit e79b2c4
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 2 deletions.
4 changes: 2 additions & 2 deletions contract-address.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@
"wmatic": "0xA108830A23A9a054FfF4470a8e6292da0886A4D4"
},
"uniswap": {
"factory": "",
"swapRouter": ""
"factory": "0x1F98431c8aD98523631AE4a59f267346ea31F984",
"swapRouter": "0xE592427A0AEce92De3Edee1F18E0157C05861564"
},
"vaultEmitter": {
"implementation": "0xbc6Fe0a018266F9F02C0E21cb21388cf1D09a560",
Expand Down
45 changes: 45 additions & 0 deletions contracts/integrations/sommelier/ISommelier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
interface ISommelier {
/**
* @notice Deposits assets into the cellar, and returns shares to receiver.
* @param assets amount of assets deposited by user.
* @param receiver address to receive the shares.
* @return shares amount of shares given for deposit.
*/
function deposit(
uint256 assets,
address receiver
) public override nonReentrant returns (uint256 shares);

/**
* @notice Withdraw assets from the cellar by redeeming shares.
* @dev Unlike conventional ERC4626 contracts, this may not always return one asset to the receiver.
* Since there are no swaps involved in this function, the receiver may receive multiple
* assets. The value of all the assets returned will be equal to the amount defined by
* `assets` denominated in the `asset` of the cellar (eg. if `asset` is USDC and `assets`
* is 1000, then the receiver will receive $1000 worth of assets in either one or many
* tokens).
* @param assets equivalent value of the assets withdrawn, denominated in the cellar's asset
* @param receiver address that will receive withdrawn assets
* @param owner address that owns the shares being redeemed
* @return shares amount of shares redeemed
*/
function withdraw(
uint256 assets,
address receiver,
address owner
) public override nonReentrant returns (uint256 shares);

/**
* @notice Simulate the effects of depositing assets at the current block, given current on-chain conditions.
* @param assets amount of assets to deposit
* @return shares that will be minted
*/
function previewDeposit(uint256 assets) public view override returns (uint256 shares);

/**
* @notice Simulate the effects of withdrawing assets at the current block, given current on-chain conditions.
* @param assets amount of assets to withdraw
* @return shares that will be redeemed
*/
function previewWithdraw(uint256 assets) public view override returns (uint256 shares);
}
151 changes: 151 additions & 0 deletions contracts/integrations/sommelier/SommelierStrategy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// SPDX-License-Identifier: UNLICENSED
// author: @stevieraykatz
pragma solidity ^0.8.19;

import {IStrategy} from "../../core/strategy/IStrategy.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {ISommelier} from "./ISommelier.sol";
import {FixedPointMathLib} from "../../lib/FixedPointMathLib.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import {IUniswapV3Factory} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract SommelierStrategy is IStrategy, Pausable, ReentrancyGuard {
using FixedPointMathLib for uint256;
using SafeERC20 for IERC20;

/*** CONSTNATS ***/
uint256 constant expScale = 1e18; // precision
address constant uniswapRouter = 0xE592427A0AEce92De3Edee1F18E0157C05861564; // swap router
uint24 constant poolFee = 500; // 0.05% fee pool
address constant tokenFeed = 0x986b5E1e1755e3C2440e960477f25201B0a8bbD4; // USDC / ETH Chainlink feed

/*** STORAGE ***/
StrategyConfig public config;

constructor(StrategyConfig memory _config) {
config = _config;
}

/*//////////////////////////////////////////////////////////////
ADMIN
//////////////////////////////////////////////////////////////*/

modifier onlyAdmin() {
if (_msgSender() != config.admin) revert AdminOnly();
_;
}

modifier nonZeroAmount(uint256 amt) {
if (amt == 0) revert ZeroAmount();
_;
}

function pause() external onlyAdmin {
_pause();
}

function unpause() external onlyAdmin {
_unpause();
}

/// @notice Check whether the contract is paused
/// @dev Make public the state of the Pausable contract's `paused` state
/// @return paused the current state of the paused boolean
function paused() public view override(IStrategy, Pausable) returns (bool) {
return super.paused();
}

/*//////////////////////////////////////////////////////////////
CONFIG
//////////////////////////////////////////////////////////////*/
/// @notice Returns the config struct
/// @return Config the current strategy config
function getStrategyConfig() external view returns (StrategyConfig memory) {
return config;
}

/// @notice Set the strategy config
/// @dev This method must be access controlled. The config overwrites the stored config in its entirety
/// @param _newConfig The StrategyConfig that willbe stored and referenced within the strategy contract
function setStrategyConfig(StrategyConfig memory _newConfig) external onlyAdmin {
config = _newConfig;
emit ConfigChanged(config);
}

/*//////////////////////////////////////////////////////////////
IMPLEMENTATION
//////////////////////////////////////////////////////////////*/
/// @notice Accepts deposits of `baseToken` and converts/trades/interacts to achieve `yieldToken`
/// @dev This method must:
/// 1) Transfer the `amt` of `config.baseToken` to this contract
/// 2) Convert the base token into the `config.yieldToken via integration-specific methods
/// 3) Set the msg.sender as approved() for the returned amt
/// @param amt the qty of `config.baseToken` that the strategy has been approved to use
/// @return yieldTokenAmt the qty of `config.yieldToken` that were yielded from the deposit action
function deposit(
uint256 amt
) external payable whenNotPaused nonReentrant nonZeroAmount(amt) returns (uint256) {

}

/// @notice Accepts `yieldTokens` and converts them back into `baseToken`
/// @dev This method must:
/// 1) Transfer the provided `amt` of `config.yieldToken` to this contract
/// 2) Convert the yield tokens provided back into the `config.baseToken via integration-specific methods
/// 3) Set the msg.sender as approved() for the returned amt
/// @param amt the qty of `config.yieldToken` that this contract has been approved to use by msg.sender
/// @return baseTokenAmt the qty of `config.baseToken` that are approved for transfer by msg.sender
function withdraw(
uint256 amt
) external payable whenNotPaused nonReentrant nonZeroAmount(amt) returns (uint256) {

}

/// @notice Provide an estimate for the current exchange rate for a given deposit
/// @dev This method expects that the `amt` provided is denominated in `baseToken`
/// @param amt the qty of the `baseToken` that should be checked for conversion rates
/// @return yieldTokenAmt the expected qty of `yieldToken` if this strategy received `amt` of `baseToken`
function previewDeposit(uint256 amt) external view returns (uint256) {

}

/// @notice Provide an estimate for the current exchange rate for a given withdrawal
/// @dev This method expects that the `amt` provided is denominated in `yieldToken`
/// @param amt the qty of the `yieldToken` that should be checked for conversion rates
/// @return yieldTokenAmt the expected qty of `baseToken` if this strategy received `amt` of `yieldToken`
function previewWithdraw(uint256 amt) external view returns (uint256) {

}

/*//////////////////////////////////////////////////////////////
INTERNAL
//////////////////////////////////////////////////////////////*/

function _enterPosition(uint256 amt) internal returns (uint256) {
try IFlux(config.yieldToken).mint(amt) returns (uint256 yieldTokens) {
return yieldTokens;
} catch Error(string memory reason) {
emit LogError(reason);
revert DepositFailed();
} catch (bytes memory data) {
emit LogErrorBytes(data);
revert DepositFailed();
}
}

function _withdrawPosition(uint256 amt) internal returns (uint256) {
try IFlux(config.yieldToken).redeem(amt) returns (uint256 baseTokens) {
return baseTokens;
} catch Error(string memory reason) {
emit LogError(reason);
revert WithdrawFailed();
} catch (bytes memory data) {
emit LogErrorBytes(data);
revert WithdrawFailed();
}
}
}
17 changes: 17 additions & 0 deletions contracts/integrations/stratConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,24 @@ export const flux: StratConfig = {
},
};

export const sommelier: StratConfig = {
name: "sommelier",
id: "0x00000002",
chainId: ChainID.ethereum,
tokenName: "RealYieldEthAP",
tokenSymbol: "YieldETH_AP",
baseToken: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
yieldToken: "0xb5b29320d2dde5ba5bafa1ebcd270052070483ec",
params: {
approvalState: StrategyApprovalState.APPROVED,
network: getNetworkNameFromChainId(ChainID.ethereum),
lockedVaultAddr: getVaultAddress("sommelier", VaultType.LOCKED),
liquidVaultAddr: getVaultAddress("sommelier", VaultType.LIQUID),
},
};

export const allStrategyConfigs: AllStratConfigs = {
dummy: dummy,
flux: flux,
sommelier: sommelier,
};

0 comments on commit e79b2c4

Please sign in to comment.