Skip to content

Instantly share code, notes, and snippets.

@sidpan1
Created October 22, 2021 07:41
Show Gist options
  • Save sidpan1/e079ead16e88630f26742784c002606c to your computer and use it in GitHub Desktop.
Save sidpan1/e079ead16e88630f26742784c002606c 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);
address investable_implementation_address = address(new CurveAaveDaiInvestableAccount());
InvestmentManager investment_manager = InvestmentManager(brew_bank.GetInvestmentManagerAddress());
investment_manager.AddInvestmentStrategy("CURVE_AAVE_STABLE_SWAP", investable_implementation_address);
}
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);
}
}
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;
function Withdraw(uint256 amount) external;
}
interface StableSwapAave {
function add_liquidity(uint[3] calldata _amounts,uint _min_mint_amount, bool _use_underlying) external returns (uint);
function remove_liquidity_one_coin(uint _token_amount, int128 _i, uint _min_amount, bool _use_underlying) external returns (uint);
}
contract BrewBank is Ownable {
uint256 public comission_rate_bps;
address public comission_address;
address public account_manager_address;
address public investment_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());
}
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 {
// Transfer the amount from the owner who has already approved this transfer.
ERC20Utils.ApprovedTransfer(amount, DAI_ADDRESS, owner());
// Approve the allowance of the curve aave smart contract.
ERC20Utils.Approve(amount, DAI_ADDRESS, CURVE_AAVE_LP_TOKEN_ADDRESS);
StableSwapAave(CURVE_AAVE_STABLE_SWAP_SMART_CONTRACT_ADDRESS).add_liquidity([amount, 0, 0], 0, false);
principal_amount = principal_amount + amount;
}
function Withdraw(uint256 lp_token_amount) public onlyOwner {
require(ERC20Utils.HasBalance(lp_token_amount, CURVE_AAVE_LP_TOKEN_ADDRESS));
StableSwapAave(CURVE_AAVE_STABLE_SWAP_SMART_CONTRACT_ADDRESS).remove_liquidity_one_coin(lp_token_amount, 0, 0, false);
uint256 withdrawn_amount = ERC20Utils.GetBalance(DAI_ADDRESS);
// 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 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());
}
}
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