diff --git a/contracts/AdditionalContractExamples.md b/contracts/AdditionalContractExamples.md new file mode 100644 index 00000000..f863252e --- /dev/null +++ b/contracts/AdditionalContractExamples.md @@ -0,0 +1,43 @@ +# Example Additional Contracts +An inventory of various contracts that can be deployed and managed via governance + +## Curve Options + +Formulas | Values +---------------------- | ----------------------------- +BancorZeroFormula.sol | BancorZeroValues.sol +SigmoidalFormula.sol | SigmoidalValues.sol +StepwiseFormula.sol | StepwiseValues.sol + +## Vault Options + +Vaults | Factories +---------------------- | ----------------------------- +Vault_ETH.sol | VaultFactory_ETH.sol +Vault_SingleAsset.sol | VaultFactory_SingleAsset.sol +Vault_Balancer.sol | VaultFactory_Balancer.sol +Vault_Yield.sol | VaultFactory_Yeild.sol + +## MigrationVaults + +From | To +---------------------- | ----------------------------- +Vault_ETH.sol | Vault_ETH.sol +Vault_ETH.sol | Vault_SingleAsset.sol +Vault_ETH.sol | Vault_Balancer.sol +Vault_ETH.sol | Vault_Yield.sol +---------------------- | ---------------------- +Vault_SingleAsset.sol | Vault_ETH.sol +Vault_SingleAsset.sol | Vault_SingleAsset.sol +Vault_SingleAsset.sol | Vault_Balancer.sol +Vault_SingleAsset.sol | Vault_Yield.sol +---------------------- | ---------------------- +Vault_Balancer.sol | Vault_ETH.sol +Vault_Balancer.sol | Vault_SingleAsset.sol +Vault_Balancer.sol | Vault_Balancer.sol +Vault_Balancer.sol | Vault_Yield.sol +---------------------- | ---------------------- +Vault_Yield.sol | Vault_ETH.sol +Vault_Yield.sol | Vault_SingleAsset.sol +Vault_Yield.sol | Vault_Balancer.sol +Vault_Yield.sol | Vault_Yield.sol \ No newline at end of file diff --git a/contracts/Admin.sol b/contracts/Admin.sol deleted file mode 100644 index 240ef3ec..00000000 --- a/contracts/Admin.sol +++ /dev/null @@ -1,109 +0,0 @@ -pragma solidity ^0.8.0; - -contract Admin { - - modifier onlyOwner() { - require(msg.sender == owner); - _; - } - - event SetMintFee(uint256 amount); - event SetTransferFee(uint256 amount); - event SetBurnFee(uint256 amount); - event SetYieldFee(uint256 amount); - event SetEarnFee(uint256 amount); - - uint256 private TRANSFERFEE_MIN = 0; - uint256 private TRANSFERFEE_MAX = 10; - uint256 private BURNFEE_MIN = 0; - uint256 private BURNFEE_MAX = 10; - uint256 private MINTFEE_MIN = 0; - uint256 private MINTFEE_MAX = 10; - uint256 private YIELDFEE_MIN = 0; - uint256 private YIELDFEE_MAX = 10; - - /// @dev for when a meToken is transferred - uint256 private _transferFee; - /// @dev for when a meToken is burned by non-owner - uint256 private _burnFee; - /// @dev for when a meToken is burned by owner - uint256 private _earnFee; - /// @dev for when a meToken is minted - uint256 private _mintFee; - /// @dev for when a meToken is first created - uint256 private _initializeFee; - /// @dev for when balanceLocked/balancePooled earns interest from other protocols - uint256 private _yieldFee; - - uint256 public owner; - - constructor() {} - - function setMintFee(uint256 amount) external onlyOwner returns (uint256) { - require(amount >= MINTFEE_MIN && amount <= MINTFEE_MAX, "out of range"); - _mintFee = amount; - emit SetMintFee(amount); - return amount; - } - - function setTransferFee(uint256 amount) external onlyOwner returns (uint256) { - require(amount >= TRANSFERFEE_MIN && amount <= TRANSFERFEE_MAX, "out of range"); - _transferFee = amount; - emit SetTransferFee(amount); - return amount; - } - - function setBurnFee(uint256 amount) external onlyOwner returns (uint256) { - require(amount >= BURNFEE_MIN && amount <= BURNFEE_MAX, "out of range"); - _burnFee = amount; - emit SetBurnFee(amount); - return amount; - } - - function setInitializeFee(uint256 amount) external onlyOwner returns (uint256) { - require(amount >= INITIALIZEFEE_MIN && amount <= INITIALIZEFEE_MAX, "out of range"); - _initializeFee = amount; - emit SetInitializeFee(amount); - return amount; - } - - function setYieldFee(uint256 amount) external onlyOwner returns (uint256) { - require(amount >= YIELDFEE_MIN && amount <= YIELDFEE_MAX, "out of range"); - _yieldFee = amount; - emit SetYieldFee(amount); - return amount; - } - - function setEarnFee(uint256 amount) external onlyOwner returns (uint256) { - require(amount >= EARNFEE_MIN && amount <= EARNFEE_MAX, "out of range"); - _earnFee = amount; - emit SetEarnFee(amount); - return amount; - } - - /// @dev for when a meToken is transferred - function transferFee() external view returns (uint256) { - return _transferFee; - } - /// @dev for when a meToken is burned by non-owner - function burnFee() external view returns (uint256) { - return _burnFee; - } - /// @dev for when a meToken is burned by owner - function earnFee() external view returns (uint256) { - return _earnFee; - } - /// @dev for when a meToken is minted - function mintFee() external view returns (uint256) { - return _mintFee; - } - /// @dev for when a meToken is first created - function initializeFee() external view returns (uint256) { - return _initializeFee; - } - /// @dev for when balanceLocked/balancePooled earns interest from other protocols - function yieldFee() external view returns (uint256) { - return _yieldFee; - } - -} \ No newline at end of file diff --git a/contracts/Curve.sol b/contracts/Curve.sol deleted file mode 100644 index 799a1278..00000000 --- a/contracts/Curve.sol +++ /dev/null @@ -1,94 +0,0 @@ -pragma solidity ^0.8.0; - -import "./Power.sol"; -import "./MeToken.sol"; - - -contract Curve is Power { - - uint256 public MAX_WEIGHT = 1000000; - - constructor() {} - - /* - @notice calculates how many tokens can be minted when current token supply = 0 - a = y / (x ^ (1/r - 1)) * Collateral ^ (1/r - 1) - */ - - function calculateMintReturnFromZero( - uint256 base_X, - uint256 base_Y, - uint256 reserveWeight, - uint256 amountEth - ) public view returns (uint256 amountMinted) { - - } - - function calculateMintReturn( - uint256 supply, - uint256 balancePool, - uint32 reserveWeight, - uint256 amountEth - ) public view returns (uint256) { - // Bancor.calculatePurchaseReturn(supply, balancePool, reserveWeight, amountEthAfterFees) - - if (amountEth == 0) { - return 0; - } - - if (supply == 0) { - uint256 exponent = 1 / reserveWeight - 1; - if (balancePool > 0) { - uint256 slope = (balancePool * (exponent + 1)) / (supply ** (exponent + 1)); - uint256 amountMinted = (amountEth / (exponent * slope)) ** reserveWeight; - return amountMinted; - } - return ;// TODO - } - - if (reserveWeight == MAX_WEIGHT) { - return supply * amountEth / balancePool; - } - - uint256 result; - uint8 precision; - uint256 baseN = amountEth + balancePool; - (result, precision) = power( - baseN, balancePool, reserveWeight, MAX_WEIGHT - ); - uint256 newTokenSupply = supply * result >> precision; - return newTokenSupply - supply; - } - - function calculateBurnReturn( - uint256 supply, - uint256 balancePool, - uint32 reserveWeight, - uint256 amountToken - ) public view returns (uint256) { - // Bancor.calculateSaleReturn(supply, balancePool, reserveWeight, amountEthAfterFees) - require(supply > 0 && reserveWeight > 0 && reserveWeight <= MAX_WEIGHT && amountToken <= supply); - - if (amountToken == 0) { - return 0; - } - - if (amountToken == supply) { - return reserveWeight; - } - - if (reserveWeight == MAX_WEIGHT) { - return balancePool * amountToken / supply; - } - - uint256 result; - uint8 precision; - uint256 baseD = supply - amountTokens; - (result, precision) = power( - supply, baseD, MAX_WEIGHT, reserveWeight - ); - uint256 oldBalance = balancePool * result; - uint256 newBalance = balancePool << precision; - return (oldBalance - newBalance) / result; - } -} \ No newline at end of file diff --git a/contracts/Fees.sol b/contracts/Fees.sol new file mode 100644 index 00000000..62e93337 --- /dev/null +++ b/contracts/Fees.sol @@ -0,0 +1,128 @@ +pragma solidity ^0.8.0; + +contract Fees { + + // NOTE: this will be the DAO + modifier onlyOwner() { + require(msg.sender == owner); + _; + } + + event SetMintFee(uint256 rate); + event SetBurnBuyerFee(uint256 rate); + event SetBurnOwnerFee(uint256 rate); + event SetTransferFee(uint256 rate); + event SetInterestFee(uint256 rate); + event SetYieldFee(uint256 rate); + event SetOwner(address owner); + event SetFeeRecipient(address recipient); + + uint256 private FEE_MAX = 10**18; + /// @dev for when a meToken is minted + uint256 private _mintFee; + /// @dev for when a meToken is burned by non-owner + uint256 private _burnBuyerFee; + /// @dev for when a meToken is burned by owner + uint256 private _burnOwnerFee; + /// @dev for when a meToken is transferred + uint256 private _transferFee; + /// @dev Generated from interest on collateral + uint256 private _interestFee; + /// @dev Generated from liquidity mining + uint256 private _yieldFee; + + address public owner; + address public feeRecipient; + + constructor( + address owner_, + uint256 mintFee_ + uint256 burnBuyerFee_ + uint256 burnOwnerFee_ + uint256 transferFee_ + uint256 interestFee_ + uint256 yieldFee_ + ) public { + owner = owner_; + _mintFee = mintFee_; + _burnBuyerFee = burnBuyerFee_; + _burnOwnerFee = burnOwnerFee_; + _transferFee = transferFee_; + _interestFee = interestFee_; + _yieldFee = yieldFee_; + } + + function setMintFee(uint256 amount) external onlyOwner { + require(amount != _mintFee && amount < FEE_MAX, "out of range"); + _mintFee = amount; + emit SetMintFee(amount); + } + + function setBurnBuyerFee(uint256 amount) external onlyOwner { + require(amount != _burnBuyerFee && amount < FEE_MAX, "out of range"); + _burnBuyerFee = amount; + emit SetBurnBuyerFee(amount); + } + + function setBurnOwnerFee(uint256 amount) external onlyOwner { + require(amount != _burnOwnerFee && amount < FEE_MAX, "out of range"); + _burnOwnerFee = amount; + emit SetBurnOwnerFee(amount); + } + + function setTransferFee(uint256 amount) external onlyOwner { + require(amount != _burnOwnerFee && amount < FEE_MAX, "out of range"); + _transferFee = amount; + emit SetTransferFee(amount); + } + + function setInterestFee(uint256 amount) external onlyOwner { + require(amount != _interestFee && amount < FEE_MAX, "out of range"); + _interestFee = amount; + emit SetInterestFee(amount); + } + + function setYieldFee(uint256 amount) external onlyOwner { + require(amount != _yieldFee && amount < FEE_MAX, "out of range"); + _yieldFee = amount; + emit SetYieldFee(amount); + } + + function setOwner(address _owner) external onlyOwner { + require(_owner != owner, "_owner == owner"); + owner = _owner; + emit SetOwner(_owner); + } + + function setFeeRecipient(address _recipient) onlyOwner { + require(feeRecipient != _recipient, "feeRecipient == _recipient"); + feeRecipient = _recipient; + emit SetFeeRecipient(_recipient); + } + + function mintFee() public view returns (uint256) { + return _mintFee; + } + + function burnBuyerFee() public view returns (uint256) { + return _burnBuyerFee; + } + + function burnOwnerFee() public view returns (uint256) { + return _burnOwnerFee; + } + + function transferFee() public view returns (uint256) { + return _transferFee; + } + + function interestFee() public view returns (uint256) { + return _interestFee; + } + + function yieldFee() public view returns (uint256) { + return _yieldFee; + } + + +} diff --git a/contracts/Hub.sol b/contracts/Hub.sol deleted file mode 100644 index 0d925290..00000000 --- a/contracts/Hub.sol +++ /dev/null @@ -1,70 +0,0 @@ -pragma solidity ^0.8.0; - -import "./Curve.sol"; - -contract Hub is Curve { - - event Donated(address indexed donor, uint256 indexed amount); - - uint256 private PERCENT = 25; - uint256 private DIVISOR = 10000; - uint256 private MAX_WEIGHT = 1000000; - - address public owner; - uint256 public base_X; - uint256 public base_Y; - // TODO: What will these balances be on initialization? - uint256 public balancePooled; - uint256 public balanceLocked; - uint256 public reserveWeight; - uint256 public refundRatio; - bool private initialized; - - constructor() {} - - function initialize( - address _owner, - uint256 _base_X, - uint256 _base_Y, - uint256 _balancePooled, - uint256 _balanceLocked, - uint256 _reserveWeight, - uint256 _refundRatio - ) external { - require(!initialized, "initialize: already initialized"); - require(_reserveWeight > 0 && _reserveWeight <= MAX_WEIGHT && _base_X > 0 && _base_Y > 0, "initialize: invalid param"); - owner = _owner; - base_X = _base_X; - base_Y = _base_Y; - balancePooled = _balancePooled; - balanceLocked = _balanceLocked; - reserveWeight = _reserveWeight; - refundRatio = _refundRatio; - - initialized = true; - } - - function mintMeToken() public payable{ - uint256 totalEth = msg.value; - } - - function burnMeToken(uint256 _amount) public payable { - - } - - function donate() public payable { - lockedBalance = lockedBalance + msg.value; - emit Donated(msg.sender, msg.value); - } - - /// @notice calculateFee is used to calculate the fee earned by the StakeOnMe Development Team whenever a MeToken Purchase or sale occurs throught contract - function calculateFee(uint256 amountEth) returns (uint256) { - return amountEth * percent / PRECISION; - } - - /// @notice calculateLockedReturn is used to calculate the amount of locked Eth returned to the owner during a burn/spend - function calculateLockedReturn(uint256 amountToken, uint256 lockedBalance, uint256 supply) returns (uint256) { - return amountToken * lockedBalance / supply; - } - -} \ No newline at end of file diff --git a/contracts/MeToken.sol b/contracts/MeToken.sol index 1499f49d..2ec5e6c8 100644 --- a/contracts/MeToken.sol +++ b/contracts/MeToken.sol @@ -1,37 +1,65 @@ pragma solidity ^0.5.0; -import "@openzeppelin/contracts/ownership/Ownables.sol"; +import "@openzeppelin/contracts/access/Ownables.sol"; import "@openzeppelin/contracts/token/ERC20/ERCBurnable.sol"; contract MeToken is Ownables, ERC20Burnable { - bool private initialized; - address public owner; - string public name; - string public symbol; + event SwitchUpdating(bool _updating); - constructor() {} + modifier onlyCreator() { + require(msg.sender == creator, "!creator"); + _; + } - function initialize( - address _owner, - string _name, - string _symbol - ) public external returns () { - require(!initialized, "initalize: already initalized"); - owner = _owner; - name = _name; - symbol = symbol; - } + modifier isUpdating() { + require(!updating, "meToken is updating"); + _; + } - function mint(address account, uint256 amount) public onlyOwner { - _mint(account, amount); - } + bool public updating; + bool private initialized; + bool private _canMigrate; + address public creator; + address public vault; + string public name; + string public symbol; - function burn(address account, uint256 value) public onlyOwner{ - _burn(account, value); - } + constructor() {} + function initialize( + address _owner, + string _name, + string _symbol + ) public { + require(!initialized, "initalize: already initalized"); + // TODO: owner has to be vault address + owner = _owner; + name = _name; + symbol = symbol; + } + function mint(address to, uint256 amount) public { + require(msg.sender == vault, "!vault"); + _mint(to, amount); + } -} + function burn(address from, uint256 value) public { + require(msg.sender == vault, "!vault"); + _burn(from, value); + } + + function canMigrate() external view returns (bool) { + return _canMigrate; + } + + /* + Scenarios when updating would happen + * only owner + * whoever is in charge of the hub + */ + function switchUpdating() returns (bool) { + require(msg.sender == 0x0); // TODO + } +} \ No newline at end of file diff --git a/contracts/curves/BancorZeroFormula.sol b/contracts/curves/BancorZeroFormula.sol new file mode 100644 index 00000000..f9d1962a --- /dev/null +++ b/contracts/curves/BancorZeroFormula.sol @@ -0,0 +1,122 @@ +pragma solidity ^0.8.0; + +import "../utils/Power.sol"; + +/** +* @title Bancor formula by Bancor +* @dev Modified from the original by Slava Balasanov +* https://github.com/bancorprotocol/contracts +* Split Power.sol out from BancorFormula.sol and replace SafeMath formulas with zeppelin's SafeMath +* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements; +* and to You under the Apache License, Version 2.0. " +*/ +contract BancorZeroFormula is Power { + + uint32 public MAX_WEIGHT = 1000000; + uint256 private PRECISION = 10**18; + + /** + * @dev given a token supply, connector balance, weight and a deposit amount (in the connector token), + * calculates the return for a given conversion (in the main token) + * + * Formula: + * Return = _supply * ((1 + _depositAmount / _balancePooled) ^ (_reserveWeight / 1000000) - 1) + * + * @param _supply token total supply + * @param _balancePooled total connector balance + * @param _reserveWeight connector weight, represented in ppm, 1-1000000 + * @param _depositAmount deposit amount, in connector token + * + * @return purchase return amount + * TODO - add if _supply = 0, then use calculateMintReturnFromZero() + */ + function _calculateMintReturn( + uint256 _supply, + uint256 _balancePooled, + uint32 _reserveWeight, + uint256 _depositAmount + ) private view returns (uint256 meTokenAmountReturned) { + // validate input + require(_supply > 0 && _balancePooled > 0 && _reserveWeight > 0 && _reserveWeight <= MAX_WEIGHT); + // special case for 0 deposit amount + if (_depositAmount == 0) { + return 0; + } + // special case if the weight = 100% + if (_reserveWeight == MAX_WEIGHT) { + return _supply * _depositAmount / _balancePooled; + } + + uint8 precision; + uint256 result; + uint256 baseN = _depositAmount + _balancePooled; + (result, precision) = power( + baseN, _balancePooled, _reserveWeight, MAX_WEIGHT + ); + uint256 newTokenSupply = _supply * result >> precision; + meTokenAmountReturned = newTokenSupply - _supply; + } + + /** + * TODO - verify function + */ + // https://www.notion.so/Economic-Modeling-f7a9e5a5a41b480490628079c794352d#6f090de4a7b34dd68d2c40b76b5f8700 + function _calculateMintReturnFromZero( + uint256 _base_x, + uint256 _base_y, + uint256 _depositAmount, + uint32 _reserveWeight + ) private view returns (uint256 meTokenAmountReturned) { + uint256 numerator = _base_y; + uint256 exponent = (PRECISION/_reserveWeight - PRECISION); + uint256 denominator = _base_x ** exponent; + meTokenAmountReturns = numerator/denominator * _depositAmount** exponent; + } + + /** + * @dev given a token supply, connector balance, weight and a sell amount (in the main token), + * calculates the return for a given conversion (in the connector token) + * + * Formula: + * Return = _balancePooled * (1 - (1 - _sellAmount / _supply) ^ (1 / (_reserveWeight / 1000000))) + * + * @param _supply token total supply + * @param _balancePooled total connector + * @param _reserveWeight constant connector Weight, represented in ppm, 1-1000000 + * @param _sellAmount sell amount, in the token itself + * + * @return sale return amount + */ + function _calculateBurnReturn( + uint256 _supply, + uint256 _balancePooled, + uint256 _sellAmount, + uint32 _reserveWeight + ) private view returns (uint256 reserveTokenAmountReturned) { + // validate input + require(_supply > 0 && _balancePooled > 0 && _reserveWeight > 0 && _reserveWeight <= MAX_WEIGHT && _sellAmount <= _supply); + // special case for 0 sell amount + if (_sellAmount == 0) { + return 0; + } + // special case for selling the entire supply + if (_sellAmount == _supply) { + return _balancePooled; + } + // special case if the weight = 100% + if (_reserveWeight == MAX_WEIGHT) { + return _balancePooled * _sellAmount / _supply; + } + + uint256 result; + uint8 precision; + uint256 baseD = _supply - _sellAmount; + (result, precision) = power( + _supply, baseD, MAX_WEIGHT, _reserveWeight + ); + uint256 oldBalance = _balancePooled * result; + uint256 newBalance = _balancePooled << precision; + + reserveTokenAmountReturned = (oldBalance - newBalance) / result; + } +} \ No newline at end of file diff --git a/contracts/curves/BancorZeroValueSet.sol b/contracts/curves/BancorZeroValueSet.sol new file mode 100644 index 00000000..6948d25f --- /dev/null +++ b/contracts/curves/BancorZeroValueSet.sol @@ -0,0 +1,139 @@ +// example of a contract `curves.libraryParameterSet` that can be registered in CurveRegistry.sol +// specifically paired with BancorFormulaFromZero.sol + +import "./BancorZeroFormula.sol"; + +contract BancorZeroFormulaValues is BancorZeroFormula { + + uint256 private PRECISION = 10**18; + + event Updated(uint256 indexed hubId); + + // NOTE: keys will be the hubId + mapping (uint256 => HubValueSet) hubValueSets; + + // NOTE: each valueSet is for a hub + struct HubValueSet { + // address hubId; // the hub that uses this parameter set + uint256 base_x; + uint256 base_y; + uint256 reserveWeight; + + bool updating; + uint256 targetValueSetId; + } + + function registerValueSet( + uint256 _hubId, uint256 _base_x, uint256 _base_y, uint256 _reserveWeight + ) { + + } + function deactivateValueSet() returns(uint256) {} + function reactivateValueSet() returns(uint256) {} + + mapping (uint256 => TargetValueSet) targetHubValueSets; + + // NOTE: for updating a hub + struct TargetHubValueSet { + uint base_x; + uint base_y; + uint256 reserveWeight; + + uint256 blockStart; + uint256 blockTarget; + bool targetReached; + } + + function registerTargetValueSet() returns(uint256) {} + + /** + * if updating == true, then reference the curve's updater.sol to linearly calculate the new rate between startBlock & targetBlock + * if updating == true and targetReached == true, then set updating == false + * needs to reference hub.vault.balancePooled + * needs to return both burnForOwner and burnForEveryoneElse values + **/ + // TODO: fix calculateMintReturn arguments + function calculateMintReturn( + uint256 _hubId, + uint256 _supply, + uint256 _balancePooled, + uint256 _depositAmount + ) view returns (uint256 amount) { + + ValueSet memory v = valueSet[_hubId]; + if (_supply > 0) { + amount = _calculateMintReturn(_supply, _balancePooled, _depositAmount, v.reserveWeight); + } else { + amount = _calculateMintReturnFromZero(v.base_x, v.base_y, _depositAmount, v.reserveWeight); + } + + if (v.updating) { + // Calculate return using weights + TargetValueSet memory t = targetValueSets[v.targetValueSetId]; + if (_supply > 0) { + uint256 targetAmount = _calculateMintReturn(_supply, _balancePooled, _depositAmount, t.reserveWeight); + } else { + uint256 targetAmount = _calculateMintReturnFromZero(t.base_x, t.base_y, _depositAmount, t.reserveWeight); + } + amount = _calculateWeightedAmount(amount, targetAmount, t); + } + } + + // TODO: _calculateBurnReturn arguments + function calculateBurnReturn( + uint256 _hubId, + uint256 _supply, + uint256 _balancePooled, + uint256 _sellAmount + ) returns (uint256 amount) { + + ValueSet memory v = valueSet[_hubId]; + amount = _calculateBurnReturn(_supply, _balancePooled, _sellAmount, v.reserveWeight); + + if (v.updating) { + // Calculate return using weights + TargetValueSet memory t = targetValueSets[v.targetValueSetId]; + uint256 targetAmount = _calculateBurnReturn(_supply, _balancePooled, _sellAmount, t.reserveWeight); + amount = _calculateWeightedAmount(amount, targetAmount, t); + } + } + + function _calculateWeightedAmount( + uint256 _amount, + uint256 _targetAmount, + TargetValueSet _t + ) private returns (uint256 weightedAmount) { + uint256 targetWeight; + + // Finish update if complete + if (_t.blockTarget <= block.number) { + _finishUpdate(t); + targetWeight = PRECISION; + } else { + uint256 targetProgress = block.number - _t.blockStart; + uint256 targetLength = _t.blockTarget - _t.blockStart; + // TODO: is this calculation right? + targetWeight = PRECISION * targetProgress / targetLength; + } + + // TODO: validate these calculations + uint256 weighted_v = _amount * (PRECISION - targetWeight); + uint256 weighted_t = _targetAmount * targetWeight; + weightedAmount = weighted_v + weighted_t; + } + + function _finishUpdate(uint256 _hubId) internal { + require(msg.sender == address(this)); + + TargetValueSet memory t = targetValueSets[_hubId]; + ValueSet memory v = valueSets[_hubId]; + + v.base_x = t.base_x; + v.base_y = t.base_y; + v.reserveWeight = t.reserveWeight; + v.updating = false; + + emit Updated(v.hubId); + } + +} \ No newline at end of file diff --git a/contracts/curves/SigmoidalFormula.sol b/contracts/curves/SigmoidalFormula.sol new file mode 100644 index 00000000..d4404d7c --- /dev/null +++ b/contracts/curves/SigmoidalFormula.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.8.0; + +contract SigmoidalFormula { + +} \ No newline at end of file diff --git a/contracts/curves/SigmoidalValueSet.sol b/contracts/curves/SigmoidalValueSet.sol new file mode 100644 index 00000000..489ea22b --- /dev/null +++ b/contracts/curves/SigmoidalValueSet.sol @@ -0,0 +1,59 @@ +// example of a contract `curves.libraryParameterSet` that can be registered in CurveRegistry.sol +// specifically paired with BancorFormulaFromZero.sol + +import "./SigmoidalFormula.sol"; + +contract SigmoidalValues is SigmoidalFormula { + + uint256 private PRECISION = 10**18; + + event Updated(uint256 indexed hubId); + + // NOTE: keys will be the hubId + mapping (uint256 => HubValueSet) hubValueSets; + + // NOTE: each valueSet is for a hub + struct HubValueSet { + // address hubId; // the hub that uses this parameter set + uint base_x; + uint base_y; + uint256 reserveWeight; + + bool updating; + uint256 targetValueSetId; + } + + function registerValueSet() returns(uint256) {} + function deactivateValueSet() returns(uint256) {} + function reactivateValueSet() returns(uint256) {} + + mapping (uint256 => TargetValueSet) targetHubValueSets; + + // NOTE: for updating a hub + struct TargetHubValueSet { + uint base_x; + uint base_y; + uint256 reserveWeight; + + uint256 blockStart; + uint256 blockTarget; + bool targetReached; + } + + function registerTargetValueSet() returns(uint256) {} + + function calculateMintReturn( + uint256 _hubId, + uint256 _supply, + uint256 _balancePooled, + uint256 _depositAmount + ) view returns (uint256 amount); } + + // TODO: _calculateBurnReturn arguments + function calculateBurnReturn( + uint256 _hubId, + uint256 _supply, + uint256 _balancePooled, + uint256 _sellAmount + ) returns (uint256 amount); +} \ No newline at end of file diff --git a/contracts/factory/MeTokenFactory.sol b/contracts/factories/MeTokenFactory.sol similarity index 62% rename from contracts/factory/MeTokenFactory.sol rename to contracts/factories/MeTokenFactory.sol index 12d3dc73..de661da9 100644 --- a/contracts/factory/MeTokenFactory.sol +++ b/contracts/factories/MeTokenFactory.sol @@ -4,13 +4,18 @@ import "../MeToken.sol"; contract MeTokenFactory { - event MeTokenCreated(address indexed _meToken, address indexed _owner, string _name, string _symbol); - - struct MeTokenDetails { - address _tokenAddress, - address _owner, + event MeTokenInitialized( + address indexed _meToken, + address indexed _owner, string _name, string _symbol + ); + + struct MeTokenDetails{ + address owner; + uint256 hub; + uint256 migrationDuration; + bool migrating; } address public owner; @@ -20,7 +25,6 @@ contract MeTokenFactory { Metoken public meToken; - constructor (address _owner) { owner = owner; } @@ -28,17 +32,18 @@ contract MeTokenFactory { function initialize( address _owner, string _name, - string _symbol + string _symbol, + uint256 hub ) public view returns (address _meToken) { require(!isOwner(_owner), "initialize: address has already created their meToken"); - meToken m = new meToken(_owner, _name, _symbol); - meTokenDetail = MeTokenDetails(m, _owner, _name, _symbol); + meToken m = new MeToken(_owner, _name, _symbol); + meTokenDetail = MeTokenDetails(m,_owner,_name,_symbol); meTokens.push(meTokenDetails); - emit meTokenCreated(m, _owner, _name, _symbol); + emit MeTokenCreated(m,_owner,_name,_symbol); return m; } @@ -47,5 +52,4 @@ contract MeTokenFactory { return owners[_address]; } - } \ No newline at end of file diff --git a/contracts/factories/VaultFactories/EXAMPLE - VaultFactory_SingleAsset.sol b/contracts/factories/VaultFactories/EXAMPLE - VaultFactory_SingleAsset.sol new file mode 100644 index 00000000..f98e64c2 --- /dev/null +++ b/contracts/factories/VaultFactories/EXAMPLE - VaultFactory_SingleAsset.sol @@ -0,0 +1,40 @@ +pragma solidity ^0.8.0; + +import "../interfaces/I_VaultRegistry.sol"; +import "../vaults/Vault_SingleAsset.sol"; + +contract VaultFactory_SingleAsset{ + + I_VaultRegistry public registry; + Vault public vault; + + constructor(address _registry) public { + require(_registry != address(0), "Cannot be 0 address"); + registry = _registry; + } + + // TODO: access control + function createVault( + string calldata name, + address _owner, + uint256 _hubId, + address _valueSetAddress, + bytes4 _encodedVaultAdditionalArgs // NOTE: this is _refundRatio and _collateralAsset hashed + ) public returns (address) { + + // create our vault + Vault memory vault = new Vault_SingleAsset(); + vault.initialize( + registry.vaultCount(), + _owner, + _hubId, + _valueSetAddress, + _encodedVaultAdditionalArgs + ); + + // Add vault to registry + registry.registerVault(name, vault, address(this)); + + return address(vault); + } +} \ No newline at end of file diff --git a/contracts/factory/HubFactory.sol b/contracts/factory/HubFactory.sol deleted file mode 100644 index d453fbb1..00000000 --- a/contracts/factory/HubFactory.sol +++ /dev/null @@ -1,38 +0,0 @@ -pragma solidity ^0.8.0; - -import "../Curve.sol"; - -contract HubFactory { - - event HubCreated(); - - struct HubDetails { - address owner, - uint256 base_X, - uint256 base_Y, - uint256 balancePooled, - uint256 balanceLocked, - uint256 reserveWeight, - uint256 refundRatio - } - - address public owner; - address public meTokenFactory; - - constructor(address _owner) { - owner = _owner; - } - - function initialize( - address _owner, - uint256 _base_X, - uint256 _base_Y, - uint256 _balancePooled, - uint256 _balanceLocked, - uint256 _reserveWeight, - uint256 _refundRatio - ) external { - require(msg.sender == owner, "initialize: !owner"); - } - -} \ No newline at end of file diff --git a/contracts/interface/I_Curve.sol b/contracts/interface/I_Curve.sol deleted file mode 100644 index b073041a..00000000 --- a/contracts/interface/I_Curve.sol +++ /dev/null @@ -1,17 +0,0 @@ -pragma solidity ^0.8.0; - -contract I_Curve { - function calculateMintReturn( - uint256 supply, - uint256 balancePool, - uint256 reserveWeight, - uint256 amountEth - ) public view returns (uint256); - - function calculateBurnReturn( - uint256 supply, - uint256 balancePool, - uint256 reserveWeight, - uint256 amountToken - ) public view returns (uint256); -} \ No newline at end of file diff --git a/contracts/interface/I_Hub.sol b/contracts/interface/I_Hub.sol deleted file mode 100644 index e69de29b..00000000 diff --git a/contracts/interfaces/I_CurveRegistry.sol b/contracts/interfaces/I_CurveRegistry.sol new file mode 100644 index 00000000..325e057e --- /dev/null +++ b/contracts/interfaces/I_CurveRegistry.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.8.0; + +interface I_CurveRegistry { + function registerCurve(string calldata name, address formula, address valueSet) external; + function registerFormula(address formula); + function registerValueSet(address valueSet); + function deactivateCurve(); // TODO + function deactivateFormula(address formula) public; + function deactivateValueSet(address valueSet) public; + function isApprovedFormula(address formula) public view returns (bool); + function isApprovedValueSet(address valueSet) public view returns (bool); + function getCurveCount() public view returns (uint256); +} \ No newline at end of file diff --git a/contracts/interfaces/I_CurveValueSet.sol b/contracts/interfaces/I_CurveValueSet.sol new file mode 100644 index 00000000..6b4db829 --- /dev/null +++ b/contracts/interfaces/I_CurveValueSet.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.8.0; + +interface I_CurveValueSet { + function calculateMintReturn( + uint256 _hub, + uint256 _supply, + uint256 _balancePooled, + uint256 _depositAmount + ) public view returns (uint256 amount); + + function calculateBurnReturn( + uint256 _hub, + uint256 _supply, + uint256 _balancePooled, + uint256 _sellAmount + ) public view returns (uint256 amount); +} \ No newline at end of file diff --git a/contracts/interfaces/I_MeToken.sol b/contracts/interfaces/I_MeToken.sol new file mode 100644 index 00000000..1bb8d14b --- /dev/null +++ b/contracts/interfaces/I_MeToken.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.8.0; + +interface I_MeToken { + function initialize(string name, address owner, string symbol) public; + function mint(address to, uint256 amount) public; + function burn(address from, uint256 amount) public; + function canMigrate() external view returns (bool); + function switchUpdating() public returns (bool); +} \ No newline at end of file diff --git a/contracts/interfaces/I_VaultFactory.sol b/contracts/interfaces/I_VaultFactory.sol new file mode 100644 index 00000000..7d62a652 --- /dev/null +++ b/contracts/interfaces/I_VaultFactory.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.8.0; + +interface I_VaultFactory { + function createVault(string calldata name, + address _owner, + uint256 _hubId, + address _valueSetAddress, + bytes4 _encodedVaultAdditionalArgs) public returns (address); +} \ No newline at end of file diff --git a/contracts/interfaces/I_VaultRegistry.sol b/contracts/interfaces/I_VaultRegistry.sol new file mode 100644 index 00000000..8c2830d4 --- /dev/null +++ b/contracts/interfaces/I_VaultRegistry.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.8.0; + +interface I_VaultRegistry { + // TODO: argument check + function registerVault(address factory, string calldata name) public; + function deactivateVault(uint256 vaultId) public; + function reactivateVault(uint256 vaultId) public; +} \ No newline at end of file diff --git a/contracts/migrations/EXAMPLE - MigrationVault_SingleAsset_to_SingleAsset.sol b/contracts/migrations/EXAMPLE - MigrationVault_SingleAsset_to_SingleAsset.sol new file mode 100644 index 00000000..f3751208 --- /dev/null +++ b/contracts/migrations/EXAMPLE - MigrationVault_SingleAsset_to_SingleAsset.sol @@ -0,0 +1,3 @@ +contract MigrationVault_SingleAsset_to_SingleAsset { + +} \ No newline at end of file diff --git a/contracts/registries/CurveRegistry.sol b/contracts/registries/CurveRegistry.sol new file mode 100644 index 00000000..accedd38 --- /dev/null +++ b/contracts/registries/CurveRegistry.sol @@ -0,0 +1,81 @@ +pragma solidity ^0.8.0; + +contract CurveRegistry { + + event RegisterCurve(string name, address formula, address values); + event RegisterFormula(address formula); + event RegisterValues(address values); + event DeactivateCurve(uint256 curveId); + event DeactivateFormula(address formula); + event DeactivateValues(address values); + + mapping (uint256 => CurveDetails) curves; + mapping (address => bool) private approvedFormula; + mapping (address => bool) private approvedValueSet; + uint256 private _curveCount; + + struct CurveDetails{ + string name; // BancorZero + address formula; // see BancorZeroFormula.sol as an example of an address that could be registered + address valueSet; // see BancorZeroValueSet.sol as an example of an address that could be registered (needs to be paired with the above library) + bool active; + } + + // TODO: access control + function registerCurve( + string calldata name, + address _formula, + address _valueSet + ) external { + require(isApprovedFormula(_formula) && isApprovedValueSet(_valueSet), "Not approved"); + + // Add curve details to storage + CurveDetails storage curveDetails = CurveDetails(name, _formula, _valueSet, true); + curves[++_curveCount] = curveDetails; + + emit RegisterCurve(name, _formula, _valueSet); + } + + function deactivateCurve() external returns(uint256) {} + + function reactivateCurve() external returns(uint256) {} + + function registerFormula(address _formula) public { + // TODO: access control + require(!isApprovedFormula(_formula), "Formula already approved"); + approvedFormula[_formula] = true; + emit RegisterFormula(_formula); + } + function registerValueSet(address _valueSet) public { + // TODO: access control + require(!isApprovedValueSet(_valueSet), "ValueSet already approved"); + approvedValueSet[_valueSet] = true; + emit RegisterValueSet(_valueSet); + } + + function deactivateFormula(address _formula) public { + // TODO: access control + require(approvedFormula[_formula], "Formula not approved"); + approvedFormula[_formula] = false; + emit DeactivateFormula(_formula); + } + + function deactivateValueSet(address _valueSet) public { + // TODO: access control + require(approvedValueSet[_valueSet], "ValueSet not approved"); + approvedValueSet[_valueSet] = false; + emit DeactivateValueSet(_valueSet); + } + + function isApprovedFormula(address formula) public view returns (bool) { + return approvedFormula[formula]; + } + + function isApprovedValueSet(address valueSet) public view returns (bool) { + return approvedValueSet[valueSet]; + } + + function getCurveCount() public view returns (uint256) { + return _curveCount; + } +} \ No newline at end of file diff --git a/contracts/registries/HubRegistry.sol b/contracts/registries/HubRegistry.sol new file mode 100644 index 00000000..f2e17b7a --- /dev/null +++ b/contracts/registries/HubRegistry.sol @@ -0,0 +1,69 @@ +pragma solidity ^0.8.0; + +import "../interfaces/I_VaultFactory.sol"; +import "../interfaces/I_VaultRegistry.sol"; +import "../interfaces/I_CurveValueSet.sol"; + + +contract HubRegistry { + + // bytes4 encoded + + event RegisterHub(address factory, string name, uint256 hubId); + event DeactivateHub(uint256 hubId); + event ReactivateHub(uint256 hubId); + + mapping (uint256 => HubDetails) private hubs; + uint256 private hubCount; + address public gov; + I_VaultRegistry public vaultRegistry; + I_CurveRegistry public curveRegistry; + + struct HubDetails { + string name; + address valueSet; + address vault; + address owner; + bool active; + } + + constructor(address _gov, address _vaultRegistry, address _curveRegistry) public { + gov = _gov; + vaultRegistry = I_VaultRegistry(_vaultRegistry); + curveRegistry = I_CurveRegistry(_curveRegistry); + } + + function registerHub( + string calldata _hubName, + address _hubOwner, + string calldata _vaultName, + address _vaultOwner, + address _vaultFactory, + address _valueSet, + bytes4 _encodedValueSetArgs, + bytes4 _encodedVaultAdditionalArgs + ) public { + require(vaultRegistry.isApprovedVaultFactory(_vaultFactory), "_vaultFactory not approved"); + require(curveRegistry.isApprovedValueSet(_valueSet), "_valueSet not approved"); + + // TODO: encode args to set the bancor value set + address valueSet = I_ValueSet(_valueSet).registerValueSet(); + + // Create new vault + vault = I_VaultFactory(_vaultFactory).createVault(_vaultName, _vaultOwner, hubCount, valueSet, _encodedVaultAdditionalArgs); + + // Add the vault to the hub + hubDetails memory h = HubDetails(name, valueSet, vault, _hubOwner, false); + + + + ++_hubCount; + } + + function deactivateHub() returns (uint256) {} + function reactivateHub() returns (uint256) {} + + function getHubCount() public view returns (uint256) { + return hubCount; + } +} \ No newline at end of file diff --git a/contracts/registries/MeTokenRegistry.sol b/contracts/registries/MeTokenRegistry.sol new file mode 100644 index 00000000..ae78b01a --- /dev/null +++ b/contracts/registries/MeTokenRegistry.sol @@ -0,0 +1,53 @@ +pragma solidity ^0.8.0; + +import "../MeToken.sol"; + +contract MeTokenRegistry{ + + modifier onlyOwner(address _meTokenAddress) { + meToken m = MeToken(address); + } + + // NOTE: hubs point at vaults, don't need vault address in struct + struct MeTokenDetails{ + address owner; + uint256 hub; + uint256 migrationDuration; + bool migrating; + } + + mapping (address => MeTokenDetails) meTokens; // key pair: ERC20 address + + // TODO: access control + function registerMeToken( + address _owner, + uint256 _hub, + uint256 _migrationDuration, + bool _migrating + ) public returns () { + MeTokenDetails memory meTokenDetails = () + } + + // TODO: access control + function initialize( + address _owner, + string _name, + string _symbol, + address hub + ) public view returns (address _meToken) { + require(!meTokens(_owner), "initialize: address has already created their meToken"); + + + meToken m = new MeToken(_owner, _name, _symbol); + + // meTokens.push(meTokenDetails); + + emit MeTokenInitialized(m,_owner,_name,_symbol); + + return m; + } + + function migrate(uint256 meTokenAddress) external onlyOwner(meTokenAddress) returns(bool) { + + } +} \ No newline at end of file diff --git a/contracts/registries/MigrationRegistry.sol b/contracts/registries/MigrationRegistry.sol new file mode 100644 index 00000000..ed63cfca --- /dev/null +++ b/contracts/registries/MigrationRegistry.sol @@ -0,0 +1,19 @@ +pragma solidity ^0.8.0; + +contract MigrationRegistry { + + mapping (uint256 => MigrationDetails) migrations; + + struct MigrationDetails { + uint256 fromHubId; + uint256 toHubId; + address migrationVault; + uint256 blockStart; + uint256 blockTarget; + bool targetReached; + } + + function registerMigration() external returns(uint256) {} + function deactivateMigration() external returns(uint256) {} + function reactivateMigration() external returns(uint256) {} +} \ No newline at end of file diff --git a/contracts/registries/VaultRegistry.sol b/contracts/registries/VaultRegistry.sol new file mode 100644 index 00000000..14cb4d39 --- /dev/null +++ b/contracts/registries/VaultRegistry.sol @@ -0,0 +1,62 @@ +pragma solidity ^0.8.0; + +contract VaultRegistry { + + event RegisterVault(string name, address vault, address factory); + event DeactivateVault(uint256 vaultId); + event ReactivateVault(uint256 vaultId); + // event RegisterFactory(address factory); + // event DeactivateFactory(address factory); + + mapping (address => bool) private approvedVaultFactory; + mapping (address => VaultDetails) public vaults; + // uint256 private _vaultCount; + + struct VaultDetails { + string name; + address factory; // reference vaultBalancerFactory.sol as an example of a vault factory + bool active; + } + + // TODO: argument check + // TODO: access control + function registerVault(string calldata name, address _vault, address _factory) { + require(isApprovedVaultFactory(_factory), "Factory not approved"); + + // Add vault details to storage + VaultDetails storage vaultDetails = VaultDetails(name, _vault, _factory, true); + vaults[_vault] = vaultDetails; + + emit RegisterVault(name, _vault, _factory); + } + + function deactivateVault(uint256 vaultId) public { + emit DeactivateVault(vaultId); + } + function reactivateVault(uint256 vaultId) public { + emit ReactivateVault(vaultId); + } + + // TODO: access control + function registerVaultFactory(address factory) public { + require(!approvedVaultFactories[factory], "Factory already approved"); + require(factory != address(0), "Factory cannot equal 0 address"); + approvedVaultFactory[factory] = true; + emit RegisterVaultFactory(factory); + } + + function deactivateVaultFactory(uint256 factory) public { + require(approvedVaultFactories[factory], "Factory not approved"); + approvedVaultFactories[factory] = false; + emit DeactivateVaultFactory(vaultId); + } + + function getVaultFactoryCount() public view returns (uint256) { + return _vaultFactoryCount; + } + + function isApprovedVaultFactory(address factory) public view returns (bool) { + return approvedVaultFactory[factory]; + } + +} \ No newline at end of file diff --git a/contracts/registry/BancorParamRegistry.sol b/contracts/registry/BancorParamRegistry.sol deleted file mode 100644 index 0f10fa51..00000000 --- a/contracts/registry/BancorParamRegistry.sol +++ /dev/null @@ -1,11 +0,0 @@ -pragma solidity ^0.8.0; - -contract BancorParamRegistry { - struct paramSet { - uint256 base_X; - uint256 base_Y; - uint256 reserveWeight; - } - - -} \ No newline at end of file diff --git a/contracts/registry/CalcRegistry.sol b/contracts/registry/CalcRegistry.sol deleted file mode 100644 index 4ff89d4a..00000000 --- a/contracts/registry/CalcRegistry.sol +++ /dev/null @@ -1,7 +0,0 @@ -pragma solidity ^0.8.0; - -contract CalcRegistry { - struct calcDetails { - CurveOption - } -} \ No newline at end of file diff --git a/contracts/registry/CurveRegistry.sol b/contracts/registry/CurveRegistry.sol deleted file mode 100644 index 6f33e9f9..00000000 --- a/contracts/registry/CurveRegistry.sol +++ /dev/null @@ -1,10 +0,0 @@ -pragma solidity ^0.8.0; - -contract CurveRegistry { - struct CurveOptions { - address libraryAddr; - string name; - bool active; - address paramRegistry; - } -} \ No newline at end of file diff --git a/contracts/Power.sol b/contracts/utils/Power.sol similarity index 91% rename from contracts/Power.sol rename to contracts/utils/Power.sol index a82a1f50..3f57e142 100644 --- a/contracts/Power.sol +++ b/contracts/utils/Power.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.5.0; +pragma solidity ^0.8.0; /** * @title Power function by Bancor @@ -44,38 +44,6 @@ contract Power { */ uint256[128] private maxExpArray; constructor() public { -// maxExpArray[0] = 0x6bffffffffffffffffffffffffffffffff; -// maxExpArray[1] = 0x67ffffffffffffffffffffffffffffffff; -// maxExpArray[2] = 0x637fffffffffffffffffffffffffffffff; -// maxExpArray[3] = 0x5f6fffffffffffffffffffffffffffffff; -// maxExpArray[4] = 0x5b77ffffffffffffffffffffffffffffff; -// maxExpArray[5] = 0x57b3ffffffffffffffffffffffffffffff; -// maxExpArray[6] = 0x5419ffffffffffffffffffffffffffffff; -// maxExpArray[7] = 0x50a2ffffffffffffffffffffffffffffff; -// maxExpArray[8] = 0x4d517fffffffffffffffffffffffffffff; -// maxExpArray[9] = 0x4a233fffffffffffffffffffffffffffff; -// maxExpArray[10] = 0x47165fffffffffffffffffffffffffffff; -// maxExpArray[11] = 0x4429afffffffffffffffffffffffffffff; -// maxExpArray[12] = 0x415bc7ffffffffffffffffffffffffffff; -// maxExpArray[13] = 0x3eab73ffffffffffffffffffffffffffff; -// maxExpArray[14] = 0x3c1771ffffffffffffffffffffffffffff; -// maxExpArray[15] = 0x399e96ffffffffffffffffffffffffffff; -// maxExpArray[16] = 0x373fc47fffffffffffffffffffffffffff; -// maxExpArray[17] = 0x34f9e8ffffffffffffffffffffffffffff; -// maxExpArray[18] = 0x32cbfd5fffffffffffffffffffffffffff; -// maxExpArray[19] = 0x30b5057fffffffffffffffffffffffffff; -// maxExpArray[20] = 0x2eb40f9fffffffffffffffffffffffffff; -// maxExpArray[21] = 0x2cc8340fffffffffffffffffffffffffff; -// maxExpArray[22] = 0x2af09481ffffffffffffffffffffffffff; -// maxExpArray[23] = 0x292c5bddffffffffffffffffffffffffff; -// maxExpArray[24] = 0x277abdcdffffffffffffffffffffffffff; -// maxExpArray[25] = 0x25daf6657fffffffffffffffffffffffff; -// maxExpArray[26] = 0x244c49c65fffffffffffffffffffffffff; -// maxExpArray[27] = 0x22ce03cd5fffffffffffffffffffffffff; -// maxExpArray[28] = 0x215f77c047ffffffffffffffffffffffff; -// maxExpArray[29] = 0x1fffffffffffffffffffffffffffffffff; -// maxExpArray[30] = 0x1eaefdbdabffffffffffffffffffffffff; -// maxExpArray[31] = 0x1d6bd8b2ebffffffffffffffffffffffff; maxExpArray[32] = 0x1c35fedd14ffffffffffffffffffffffff; maxExpArray[33] = 0x1b0ce43b323fffffffffffffffffffffff; maxExpArray[34] = 0x19f0028ec1ffffffffffffffffffffffff; diff --git a/contracts/vaults/SingleAsset.sol b/contracts/vaults/SingleAsset.sol new file mode 100644 index 00000000..7a15bb2b --- /dev/null +++ b/contracts/vaults/SingleAsset.sol @@ -0,0 +1,156 @@ +pragma solidity ^0.8.0; + +import "./Vault.sol"; +import "../Fees.sol"; +import "../MeToken.sol"; +import "../registries/MeTokenRegistry.sol"; +import "../registries/HubRegistry.sol"; +import "../registries/CurveRegistry.sol"; + + +import "../interfaces/I_CurveValueSet.sol"; +import "../interfaces/I_ERC20.sol"; // TODO +import "../interfaces/I_MeToken.sol"; + +contract SingleAsset is Vault, Fees, MeTokenRegistry, HubRegistry, CurveRegistry { + + bytes4 private encodedInitializeFunc = bytes(keccak256("_initialize(uint256,address)")); + + uint256 private PRECISION = 10**18; + uint256 public id; + address public owner; + uint256 public collateralBalance; + uint256 public hubId; + uint256 public refundRatio; + I_ERC20 public collateralAsset; + I_CurveValueSet public curve; + + mapping (address => MeTokenBalance) meTokenBalances; + + struct MeTokenBalance { + uint256 supply; + uint256 balancePooled; + uint256 balanceLocked; + bool active; + } + + event SetCurve(address curveValueSet); + + constructor() {} + + function initialize( + uint256 _id, + address _owner, + uint256 _hubId, + address _curveValueSet, + bytes4 encodedArgs // NOTE: this is _refundRatio and _collateralAsset hashed + ) public onlyVaultFactory { // TODO: onlyVaultFactory + require(!initialized, "already initialized"); + require(_refundRatio < PRECISION, "_refundRatio >= PRECISION"); + id = _id; + owner = _owner; + hubId = _hubId; // TODO: require hubId exists + curve = I_CurveValueSet(_curveValueSet); // TODO: check valueSet approved? + + require(this.call(encodedInitializeFunc, encodedArgs), "Encoding failed"); + + initialized = true; + } + + function _initialize(uint256 _refundRatio, address _collateralAsset) private { + refundRatio = _refundRatio; + collateralAsset = I_ERC20(_collateralAsset); + } + + /** + * passes _valueSet through hub.curveDetails.values.calculateMintReturn() and ~.calculateBurnReturn() + **/ + // TODO: http://coders-errand.com/hash-functions-for-smart-contracts-part-3/ + function mint(uint _amount, address _meToken) { + require(initialized, "!initialized"); + + MeTokenBalance memory mt = meTokenBalances[_meToken]; + require(mt.active, "MeToken is not active"); + + // TODO: validate mintFee() is proportional to PRECISION + uint256 feeAmount = _amount * mintFee() / PRECISION; + uint256 amountAfterFees = _amount - feeAmount; + + // Calculate how much meToken is minted + amountMinted = valuesContract.calculateMintReturn( + hubId, + mt.supply, + mt.balancePooled, + amountAfterFees + ); + + // Update balances + collateralBalance = collateralBalance + amountAfterFees; + mt.balancePooled = mt.balancePooled + amountAfterFees; + mt.supply = mt.supply + amountMinted; + + // Send fees to recipient (TODO: validate feeRecipient()) + collateralAsset.transferFrom(msg.sender, feeRecipient(), feeAmount); + + // Send collateral to vault + collateralAsset.transferFrom(msg.sender, address(this), amountAfterFees); + + // Mint meToken + I_MeToken(_meToken).mint(msg.sender, amountMinted); + } + + function burn(uint256 _amount, address _meToken){ + require(intialized, "!initialized"); + + MeTokenBalance memory mt = meTokenBalances[_meToken]; + require(mt.active, "MeToken is not active"); + + uint256 feeAmount; + uint256 amountAfterFees; + + if (msg.sender = meToken.owner) { + feeAmount = _amount * burnOwnerFee() / PRECISION; + amountAfterFees = _amount - feeAmount + earnedfromLocked; + + uint256 amountFromLocked = _amount * mt.lockedBalance / mt.supply; + + // decrease balance locked + mt.balanceLocked = mt.balanceLocked - earnedfromLocked; + + } else { // burner is not owner + + feeAmount = _amount * burnBuyerFee() / PRECISION; + amountAfterFees = _amount - feeAmount; + + uint256 amountToLock = amountAfterFees * refundRatio / PRECISION; + uint256 amount + + // increase balance locked + mt.balanceLocked = mt.balanceLocked + amountToLock; + } + + + _meToken.transfer(feeRecipient(), feeAmount); + _meToken.transferFrom(msg.sender, toUser, toUser); + mt.balancePooled -- _amount; + } + + + /// @notice calculateLockedReturn is used to calculate the amount of locked Eth returned to the owner during a burn/spend + function calculateLockedReturn(address _meToken, uint256 amountToken) returns (uint256) { + return amountToken * lockedBalance / supply; + } + + // TODO - figure out governance of updating the collateralAsset in a vault + function setCollateralAsset(address _collateralAsset) public onlyGov { + require(initialized, "!initialized"); + } + + // TODO: onlyGov modifier + function setCurve(address _newCurveValueSet) public onlyGov { + require(initialized, "!initialized"); + require(_newCurveValueSet != address(curve), "Same address"); + curve = I_CurveValueSet(_newCurveValueSet); + emit SetCurve(_newCurveValueSet); + } +} \ No newline at end of file diff --git a/contracts/vaults/Vault.sol b/contracts/vaults/Vault.sol new file mode 100644 index 00000000..c715a15a --- /dev/null +++ b/contracts/vaults/Vault.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.8.0; + +contract Vault { + + uint256 hub; + address collateralAsset; + + mapping (address => MeTokenBalances) MeTokenBalances; + + struct MeTokenBalances{ + uint256 balancePooled; + uint256 balanceLocked; + } + + /** + * TODO - figure out governance of updating the collateralAsset in a vault + **/ + function updateCollateralAsset () onlyGov returns() {} + + /** + * passes _valueSet through hub.curves.values.calculateMintReturn() and ~.calculateBurnReturn() + **/ + function mint(_valueSet, _meToken) returns() {} \ No newline at end of file