Skip to content

Instantly share code, notes, and snippets.

@sidpan1
Created October 22, 2021 13:23
Show Gist options
  • Save sidpan1/f52b5d1416e84fda3da95f594f5c2c4a to your computer and use it in GitHub Desktop.
Save sidpan1/f52b5d1416e84fda3da95f594f5c2c4a to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.9+commit.e5eed63a.js&optimize=true&runs=200&gist=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
contract BrewSmartContractTest {
BrewBank public brew_bank;
address public account_address;
function CreateBrewBank() public {
brew_bank = new BrewBank(300, 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4);
InvestmentManager investment_manager = InvestmentManager(brew_bank.GetInvestmentManagerAddress());
investment_manager.AddInvestmentStrategy("CURVE_AAVE_STABLE_SWAP", address(new CurveAaveDaiInvestableAccount()));
investment_manager.AddInvestmentStrategy("CURVE_AAVE_STABLE_SWAP_WITH_REWARDS", address(new CurveAaveDaiInvestableAccountWithRewards()));
}
function CreateAccount () public {
account_address = brew_bank.CreateAccount(address(this), "CURVE_AAVE_STABLE_SWAP");
Investable account = Investable(account_address);
account.Deposit(20);
account.Withdraw(10);
}
function CreateAccount1 () public {
account_address = brew_bank.CreateAccount(address(this), "CURVE_AAVE_STABLE_SWAP_WITH_REWARDS");
Investable account = Investable(account_address);
account.Deposit(20);
account.Withdraw(10);
}
}
interface AccountFactory {
function CreateAccount(address account_template_address) external returns (address);
}
interface AccountManager {
function CreateAccount(address beneficiary_address, uint256 comission_rate_bps, address investment_manager_address, address account_template_address) external returns (address);
}
interface InvestmentManager {
function AddInvestmentStrategy(string memory strategy_name, address investable_implementation_contract) external;
function GetInvestmentStrategy(string memory strategy_name) external view returns (address);
}
interface Investable {
function Deposit(uint256 amount) external returns (uint256);
function Withdraw(uint256 amount) external;
}
interface StableSwapAave {
function add_liquidity(uint[3] calldata _amounts,uint _min_mint_amount, bool _use_underlying) external returns (uint256);
function remove_liquidity_one_coin(uint _token_amount, int128 _i, uint _min_amount, bool _use_underlying) external returns (uint256);
}
interface LiquidityGauge {
function deposit(uint256 _value, address _addr, bool _claim_rewards) external;
function withdraw(uint256 _value, bool _claim_rewards) external;
function claim_rewards() external;
}
interface OneSplitAudit { // interface for 1inch exchange.
function getExpectedReturn (
IERC20 fromToken,
IERC20 toToken,
uint256 amount,
uint256 parts,
uint256 disableFlags
)
external
view
returns(
uint256 returnAmount,
uint256[] memory distribution
);
function swap(
IERC20 fromToken,
IERC20 toToken,
uint256 amount,
uint256 minReturn,
uint256[] memory distribution,
uint256 disableFlags
) external payable;
}
interface ExchangeManager {
function exchange(address from_token, address to_token, uint256 amount) external payable returns (uint256);
}
contract BrewBank is Ownable {
uint256 public comission_rate_bps;
address public comission_address;
address public account_manager_address;
address public investment_manager_address;
address public exchange_manager_address;
constructor (uint256 _comission_rate_bps, address _comission_address) {
comission_rate_bps = _comission_rate_bps;
comission_address = _comission_address;
address account_factory_address = address(new BrewAccountFactory());
account_manager_address = address(new BrewAccountManager(account_factory_address));
BrewAccountFactory(account_factory_address).transferOwnership(account_manager_address);
investment_manager_address = address(new BrewInvestmentManager());
BrewInvestmentManager(investment_manager_address).transferOwnership(owner());
exchange_manager_address = address(new Brew1InchExchangeManager());
}
function CreateAccount(address beneficiary_address, string memory investment_strategy_name) public onlyOwner returns (address) {
address account_template_address = InvestmentManager(investment_manager_address).GetInvestmentStrategy(investment_strategy_name);
address account_address = AccountManager(account_manager_address).CreateAccount(beneficiary_address, comission_rate_bps, comission_address, account_template_address);
return account_address;
}
function GetAccountManagerAddress() public view returns (address) {
return account_manager_address;
}
function GetInvestmentManagerAddress() public view returns (address) {
return investment_manager_address;
}
}
contract BrewAccountManager is AccountManager, Ownable {
address public account_factory_address;
mapping(address => address) beneficiary_account_address_map;
constructor (address _account_factory_address) {
account_factory_address = _account_factory_address;
}
function CreateAccount(address beneficiary_address, uint256 comission_rate_bps, address comission_address, address account_template_address) public onlyOwner returns (address) {
address account_address = AccountFactory(account_factory_address).CreateAccount(account_template_address);
ComissionableAccount account = ComissionableAccount(account_address);
account.Initialize(comission_rate_bps, comission_address, address(this));
account.transferOwnership(beneficiary_address);
beneficiary_account_address_map[beneficiary_address] = account_address;
return account_address;
}
function GetAccount(address beneficiary_address) public view returns (address) {
address account_address = beneficiary_account_address_map[beneficiary_address];
require(account_address != address(0), "This beneficiary_address does not exist.");
return account_address;
}
}
contract BrewAccountFactory is AccountFactory, Ownable {
using Clones for address;
function CreateAccount(address account_template_address) public onlyOwner returns (address) {
require(account_template_address != address(0), "account_template must be set");
address account_address = account_template_address.clone();
return account_address;
}
}
contract BrewInvestmentManager is InvestmentManager, Ownable {
mapping(string => address) public investment_strategies;
// New investment strategies can be added at runtime, but older ones can not be edited.
function AddInvestmentStrategy(string memory strategy_name, address investable_implementation_contract) public onlyOwner {
require(investment_strategies[strategy_name] == address(0), "This investment strategy name is already added");
investment_strategies[strategy_name] = investable_implementation_contract;
}
function GetInvestmentStrategy(string memory strategy_name) public view returns (address) {
address investable_implementation_address = investment_strategies[strategy_name];
require(investable_implementation_address != address(0), "This investment strategy name does not exist");
return investable_implementation_address;
}
}
contract ComissionableAccount is Initializable, OwnableUpgradeable {
uint256 public comission_rate_bps;
address public comission_address;
address public account_manager_address;
function Initialize(uint256 _comission_rate_bps, address _comission_address, address _account_manager_address) public initializer {
comission_rate_bps = _comission_rate_bps;
comission_address = _comission_address;
account_manager_address = _account_manager_address;
__Ownable_init();
}
function CalculateComission(uint256 amount) internal view returns (uint256){
return (amount * comission_rate_bps)/10000;
}
}
contract CurveAaveDaiInvestableAccount is Investable, ComissionableAccount {
address public DAI_ADDRESS = 0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063;
address public CURVE_AAVE_LP_TOKEN_ADDRESS = 0xE7a24EF0C5e95Ffb0f6684b813A78F2a3AD7D171;
address public CURVE_AAVE_STABLE_SWAP_SMART_CONTRACT_ADDRESS = 0x445FE580eF8d70FF569aB36e80c647af338db351;
uint256 public principal_amount;
function Deposit(uint256 amount) public onlyOwner virtual returns (uint256) {
// Transfer the amount from the owner who has already approved this transfer.
ERC20Utils.ApprovedTransfer(amount, DAI_ADDRESS, owner());
return DepositFromSelf(amount);
}
function DepositFromSelf(uint256 amount) internal returns (uint256) {
// Approve the allowance of the curve aave smart contract.
ERC20Utils.Approve(amount, DAI_ADDRESS, CURVE_AAVE_LP_TOKEN_ADDRESS);
uint256 minted_lp_token_amount = StableSwapAave(CURVE_AAVE_STABLE_SWAP_SMART_CONTRACT_ADDRESS).add_liquidity([amount, 0, 0], 0, false);
principal_amount = principal_amount + amount;
return minted_lp_token_amount;
}
function Withdraw(uint256 lp_token_amount) public onlyOwner virtual {
require(ERC20Utils.HasBalance(lp_token_amount, CURVE_AAVE_LP_TOKEN_ADDRESS));
uint256 withdrawn_amount = StableSwapAave(CURVE_AAVE_STABLE_SWAP_SMART_CONTRACT_ADDRESS).remove_liquidity_one_coin(lp_token_amount, 0, 0, false);
// Charge comission only if the withdrawn amount exceeds the principal amount.
if (withdrawn_amount <= principal_amount) {
TransferWithoutComission(withdrawn_amount);
// Reduce the principal amount by the withdran amount.
principal_amount = principal_amount - withdrawn_amount;
} else {
uint256 excess_amount = withdrawn_amount - principal_amount;
uint256 comission_amount = CalculateComission(excess_amount);
TransferWithComission(withdrawn_amount, comission_amount);
principal_amount = 0;
}
}
function WithdrawAll() public onlyOwner virtual {
uint256 total_amount = ERC20Utils.GetBalance(CURVE_AAVE_LP_TOKEN_ADDRESS);
Withdraw(total_amount);
}
function TransferWithComission(uint256 total_amount, uint256 comission_amount) internal {
ERC20Utils.Transfer(comission_amount, DAI_ADDRESS, comission_address);
ERC20Utils.Transfer(total_amount - comission_amount, DAI_ADDRESS, owner());
}
function TransferWithoutComission(uint256 total_amount) internal {
ERC20Utils.Transfer(total_amount, DAI_ADDRESS, owner());
}
}
contract CurveAaveDaiInvestableAccountWithRewards is CurveAaveDaiInvestableAccount {
address public LIQUIDITY_GAUGE_CONTRACT_ADDRESS = 0x19793B454D3AfC7b454F206Ffe95aDE26cA6912c;
address public LIQUIDITY_GAUGE_TOKEN_ADDRESS = 0x19793B454D3AfC7b454F206Ffe95aDE26cA6912c;
address public WMATIC_TOKEN_ADDRESS = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270;
address public CRV_TOKEN_ADDRESS = 0x172370d5Cd63279eFa6d502DAB29171933a610AF;
uint256 public reinvested_amount;
function Deposit(uint256 amount) public override onlyOwner returns (uint256) {
uint256 minted_lp_token_amount = CurveAaveDaiInvestableAccount.Deposit(amount);
// Approve the allowance of liquidity gauge smart contract.
ERC20Utils.Approve(amount, CURVE_AAVE_LP_TOKEN_ADDRESS, LIQUIDITY_GAUGE_CONTRACT_ADDRESS);
LiquidityGauge(LIQUIDITY_GAUGE_CONTRACT_ADDRESS).deposit(minted_lp_token_amount, address(this), false);
return minted_lp_token_amount;
}
function Withdraw(uint256 gauge_token_amount) public override onlyOwner {
LiquidityGauge(LIQUIDITY_GAUGE_CONTRACT_ADDRESS).withdraw(gauge_token_amount, false);
CurveAaveDaiInvestableAccount.Withdraw(gauge_token_amount);
}
function WithdrawAll(address exchange_manager_address) public onlyOwner {
CurveAaveDaiInvestableAccount.WithdrawAll();
uint256 return_amount = ClaimAndExchangeRewards(exchange_manager_address);
WithdrawRewardsAfterComission(return_amount);
}
function ReinvestRewards(address exchange_manager_address) public {
uint256 return_amount = ClaimAndExchangeRewards(exchange_manager_address);
ReinvestRewardsAfterComission(return_amount);
}
function ClaimAndExchangeRewards(address exchange_manager_address) public returns (uint256) {
LiquidityGauge(LIQUIDITY_GAUGE_CONTRACT_ADDRESS).claim_rewards();
uint256 return_amount = 0;
return_amount = return_amount + ExchangeRewards(exchange_manager_address, WMATIC_TOKEN_ADDRESS);
return_amount = return_amount + ExchangeRewards(exchange_manager_address, CRV_TOKEN_ADDRESS);
return return_amount;
}
function ExchangeRewards(address exchange_manager_address, address reward_token_address) internal returns (uint256){
ExchangeManager exchange_manager = ExchangeManager(exchange_manager_address);
uint256 balance_amount = ERC20Utils.GetBalance(reward_token_address);
return exchange_manager.exchange(WMATIC_TOKEN_ADDRESS, DAI_ADDRESS, balance_amount);
}
function WithdrawRewardsAfterComission(uint256 return_amount) internal {
uint256 comission_amount = CalculateComission(return_amount);
TransferWithComission(return_amount, comission_amount);
}
function ReinvestRewardsAfterComission(uint256 return_amount) internal {
uint256 comission_amount = CalculateComission(return_amount);
uint256 reinvest_amount = return_amount - comission_amount;
CurveAaveDaiInvestableAccount.DepositFromSelf(reinvest_amount);
ERC20Utils.Transfer(comission_amount, DAI_ADDRESS, comission_address);
reinvested_amount = reinvested_amount + reinvest_amount;
}
}
contract Brew1InchExchangeManager is ExchangeManager {
address ONE_INCH_SMART_CONTRACT_ADDRESS = 0xC586BeF4a0992C495Cf22e1aeEE4E446CECDee0E;
uint256 PARTS = 10;
uint256 FLAGS = 0;
function exchange(address from_token, address to_token, uint256 amount) public payable returns (uint256){
ERC20Utils.Approve(amount, from_token, ONE_INCH_SMART_CONTRACT_ADDRESS);
(uint256 return_amount, uint256[] memory distribution) = OneSplitAudit(ONE_INCH_SMART_CONTRACT_ADDRESS).getExpectedReturn(IERC20(from_token), IERC20(to_token), amount, PARTS, FLAGS);
OneSplitAudit(ONE_INCH_SMART_CONTRACT_ADDRESS).swap(IERC20(from_token), IERC20(to_token), amount, return_amount, distribution, FLAGS);
return return_amount;
}
}
library ERC20Utils {
function Approve(uint256 amount, address token_address, address spender) internal {
IERC20(token_address).approve(spender, amount);
}
function ApprovedTransfer(uint256 amount, address token_address, address sender) internal {
IERC20(token_address).transferFrom(sender, address(this), amount);
}
function Transfer(uint256 amount, address token_address, address recipient) internal {
IERC20(token_address).transfer(recipient, amount);
}
function GetBalance(address token_address) internal view returns (uint256) {
return IERC20(token_address).balanceOf(address(this));
}
function HasBalance(uint256 amount, address token_address) internal view returns (bool) {
return IERC20(token_address).balanceOf(address(this)) >= amount;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment