-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
336 additions
and
14 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,171 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.25; | ||
|
||
import { IComponentToken } from "./interfaces/IComponentToken.sol"; | ||
import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; | ||
|
||
/** | ||
* @title AggregateToken | ||
* @dev ERC20 token that represents a basket of other ERC20 tokens | ||
* Invariant: the total value of all AggregateTokens minted is equal to the total value of all of its component tokens | ||
*/ | ||
contract AggregateToken is IComponentToken, ERC20Upgradeable { | ||
IERC20 public currencyToken; | ||
IComponentToken[] public componentTokens; | ||
string public tokenURI; | ||
|
||
// Base at which we do calculations in order to minimize rounding differences | ||
uint256 _BASE = 10 ** 18; | ||
|
||
// Price at which the vault manager is willing to sell the aggregate token, times the base | ||
uint256 askPrice; | ||
/* Price at which the vault manager is willing to buy back the aggregate token, times the base | ||
* This is always smaller than the ask price, so if the vault manager never changes either price, | ||
* then they will always be able to buy back all outstanding AggregateTokens at a profit | ||
*/ | ||
uint256 bidPrice; | ||
|
||
uint8 private _currencyDecimals; | ||
uint8 private _decimals; | ||
|
||
// Events | ||
|
||
/** | ||
* @dev Emitted when a user buys aggregateToken using currencyToken | ||
* @param user Address of the user who buys the aggregateToken | ||
* @param currencyTokenAmount Amount of currencyToken paid | ||
* @param aggregateTokenAmount Amount of aggregateToken bought | ||
*/ | ||
event Buy(address indexed user, uint256 currencyTokenAmount, uint256 aggregateTokenAmount); | ||
|
||
/** | ||
* @dev Emitted when a user sells aggregateToken for currencyToken | ||
* @param user Address of the user who sells the aggregateToken | ||
* @param currencyTokenAmount Amount of currencyToken received | ||
* @param aggregateTokenAmount Amount of aggregateToken sold | ||
*/ | ||
event Sell(address indexed user, uint256 currencyTokenAmount, uint256 aggregateTokenAmount); | ||
|
||
function initialize( | ||
string memory name, | ||
string memory symbol, | ||
uint8 __decimals, | ||
string memory _tokenURI, | ||
address _currencyToken, | ||
address[] _componentTokens, | ||
uint256 _askPrice, | ||
uint256 _bidPrice | ||
) public initializer { | ||
ERC20__init(name, symbol); | ||
_decimals = __decimals; | ||
tokenURI = _tokenURI; | ||
currencyToken = IERC20(_currencyToken); | ||
_currencyDecimals = currencyToken.decimals(); | ||
componentTokens = _componentTokens; // TODO initialize the array | ||
askPrice = _askPrice; | ||
bidPrice = _bidPrice; | ||
} | ||
|
||
// Override Functions | ||
|
||
/** | ||
* @notice Returns the number of decimals of the aggregateToken | ||
*/ | ||
function decimals() public view override returns (uint8) { | ||
return _decimals; | ||
} | ||
|
||
// User Functions | ||
|
||
/** | ||
* @notice Buy the aggregateToken using currencyToken | ||
* @dev The user must approve the contract to spend the currencyToken | ||
* @param currencyTokenAmount Amount of currencyToken to pay for the aggregateToken | ||
*/ | ||
function buy( | ||
uint256 currencyTokenAmount | ||
) public { | ||
// TODO: figure decimals math | ||
uint256 aggregateTokenAmount = currencyTokenAmount * _BASE / askPrice; | ||
|
||
require(currencyToken.transferFrom(msg.sender, address(this), currencyTokenAmount), "AggregateToken: failed to transfer currencyToken"); | ||
_mint(msg.sender, aggregateTokenAmount); | ||
|
||
emit Buy(msg.sender, currencyTokenAmount, aggregateTokenAmount); | ||
} | ||
|
||
/** | ||
* @notice Sell the aggregateToken to receive currencyToken | ||
* @param currencyTokenAmount Amount of currencyToken to receive for the aggregateToken | ||
*/ | ||
function sell( | ||
uint256 currencyTokenAmount | ||
) public { | ||
// TODO: figure decimals math | ||
uint256 aggregateTokenAmount = currencyTokenAmount * _BASE / bidPrice; | ||
|
||
require(currencyToken.transfer(msg.sender, currencyTokenAmount), "AggregateToken: failed to transfer currencyToken"); | ||
_burn(msg.sender, aggregateTokenAmount); | ||
|
||
emit Sell(msg.sender, currencyTokenAmount, aggregateTokenAmount); | ||
} | ||
|
||
/** | ||
* @notice | ||
*/ | ||
function claim(uint256 amount) public { | ||
// TODO - rebasing vs. streaming | ||
} | ||
|
||
function claimAll() public { | ||
uint256 amount = claimableAmount(msg.sender); | ||
claim(amount); | ||
} | ||
|
||
// Admin Functions | ||
|
||
function buyComponentToken(address token, uint256 amount) public onlyOwner { | ||
// TODO verify it's allowed | ||
IComponentToken(token).buy(amount); | ||
} | ||
|
||
function sellComponentToken(address token, uint256 amount) public onlyOwner { | ||
IComponentToken(token).sell(amount); | ||
} | ||
|
||
// Admin Setter Functions | ||
|
||
function setTokenURI(string memory uri) public onlyOwner { | ||
tokenURI = uri; | ||
} | ||
|
||
function addAllowedComponentToken( | ||
address token, | ||
) public onlyOwner { | ||
componentTokens.push(IComponentToken(token)); | ||
} | ||
|
||
function removeAllowedComponentToken( | ||
address token, | ||
) public onlyOwner { | ||
componentTokens.push(IComponentToken(token)); | ||
} | ||
|
||
function setAskPrice(uint256 price) public onlyOwner { | ||
askPrice = price; | ||
} | ||
|
||
function setBidPrice(uint256 price) public onlyOwner { | ||
bidPrice = price; | ||
} | ||
|
||
// View Functions | ||
|
||
function claimableAmount(address user) public view returns (uint256 amount) { | ||
amount = 0; | ||
} | ||
|
||
function allowedComponentTokens() public view returns (address[] memory) { | ||
|
||
} | ||
} |
This file was deleted.
Oops, something went wrong.
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,137 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.25; | ||
|
||
import { IComponentToken } from "./interfaces/IComponentToken.sol"; | ||
import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; | ||
|
||
/** | ||
* @title ComponentTokenExample | ||
* @dev ERC20 token that represents a component of an AggregateToken | ||
* Invariant: the total value of all ComponentTokens minted is equal to the total value of all of its component tokens | ||
*/ | ||
contract ComponentToken is IComponentToken, ERC20Upgradeable { | ||
IERC20 public currencyToken; | ||
|
||
// Base at which we do calculations in order to minimize rounding differences | ||
uint256 _BASE = 10 ** 18; | ||
|
||
// Price at which the vault manager is willing to sell the aggregate token, times the base | ||
uint256 askPrice; | ||
/* Price at which the vault manager is willing to buy back the aggregate token, times the base | ||
* This is always smaller than the ask price, so if the vault manager never changes either price, | ||
* then they will always be able to buy back all outstanding ComponentTokens at a profit | ||
*/ | ||
uint256 bidPrice; | ||
|
||
uint8 private _currencyDecimals; | ||
uint8 private _decimals; | ||
|
||
// Events | ||
|
||
/** | ||
* @dev Emitted when a user stakes currencyToken to receive aggregateToken in return | ||
* @param user Address of the user who staked the currencyToken | ||
* @param currencyTokenAmount Amount of currencyToken staked | ||
* @param aggregateTokenAmount Amount of aggregateToken received | ||
*/ | ||
event Buy(address indexed user, uint256 currencyTokenAmount, uint256 aggregateTokenAmount); | ||
|
||
/** | ||
* @dev Emitted when a user unstakes aggregateToken to receive currencyToken in return | ||
* @param user Address of the user who unstaked the aggregateToken | ||
* @param currencyTokenAmount Amount of currencyToken received | ||
* @param aggregateTokenAmount Amount of aggregateToken unstaked | ||
*/ | ||
event Sell(address indexed user, uint256 currencyTokenAmount, uint256 aggregateTokenAmount); | ||
|
||
function initialize( | ||
string memory name, | ||
string memory symbol, | ||
uint8 __decimals, | ||
string memory _tokenURI, | ||
address _currencyToken, | ||
uint256 _askPrice, | ||
uint256 _bidPrice | ||
) public initializer { | ||
tokenURI = _tokenURI; | ||
|
||
currencyToken = IERC20(_currencyToken); | ||
_currencyDecimals = currencyToken.decimals(); | ||
askPrice = _askPrice; | ||
bidPrice = _bidPrice; | ||
_decimals = __decimals; | ||
} | ||
|
||
// Override Functions | ||
|
||
/** | ||
* @notice Returns the number of decimals of the aggregateToken | ||
*/ | ||
function decimals() public view override returns (uint8) { | ||
return _decimals; | ||
} | ||
|
||
// User Functions | ||
|
||
/** | ||
* @notice Stake the currencyToken to receive aggregateToken in return | ||
* @dev The user must approve the contract to spend the currencyToken | ||
* @param currencyTokenAmount Amount of currencyToken to stake | ||
*/ | ||
function buy( | ||
address currencyToken, | ||
uint256 currencyTokenAmount | ||
) public { | ||
/* | ||
// TODO: figure decimals math | ||
uint256 aggregateTokenAmount = currencyTokenAmount * _BASE / askPrice; | ||
require(currencyToken.transferFrom(msg.sender, address(this), currencyTokenAmount), "AggregateToken: failed to transfer currencyToken"); | ||
_mint(msg.sender, aggregateTokenAmount); | ||
emit Staked(msg.sender, currencyTokenAmount, aggregateTokenAmount); | ||
*/ | ||
|
||
DEX.swap(p, address(this)); | ||
|
||
} | ||
|
||
/** | ||
* @notice Unstake the aggregateToken to receive currencyToken in return | ||
* @param currencyTokenAmount Amount of currencyToken to receive | ||
*/ | ||
function ( | ||
address currencyToken, | ||
uint256 currencyTokenAmount | ||
) public { | ||
// TODO: figure decimals math | ||
uint256 aggregateTokenAmount = currencyTokenAmount * _BASE / bidPrice; | ||
|
||
require(currencyToken.transfer(msg.sender, currencyTokenAmount), "AggregateToken: failed to transfer currencyToken"); | ||
_burn(msg.sender, aggregateTokenAmount); | ||
|
||
emit Unstaked(msg.sender, currencyTokenAmount, aggregateTokenAmount); | ||
} | ||
|
||
/** | ||
* @notice | ||
*/ | ||
function claim(uint256 amount) public { | ||
// TODO - rebasing vs. streaming | ||
} | ||
|
||
function claimAll() public { | ||
uint256 amount = claimableAmount(msg.sender); | ||
claim(amount); | ||
} | ||
|
||
// Admin Setter Functions | ||
|
||
function setAskPrice(uint256 price) public onlyOwner { | ||
askPrice = price; | ||
} | ||
|
||
function setBidPrice(uint256 price) public onlyOwner { | ||
bidPrice = price; | ||
} | ||
} |
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,13 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.25; | ||
|
||
contract NestStaking { | ||
|
||
function createAggregateToken( | ||
string memory name, | ||
string memory symbol, | ||
uint8 decimals, | ||
) public returns (address) { | ||
return address(0); | ||
} | ||
} |
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,6 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.25; | ||
|
||
interface IComponentToken { | ||
function setTokenURI(string memory uri) external; | ||
} |
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 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.25; | ||
|
||
interface IComponentToken is IERC20 { | ||
function buy(uint256 amount) external; | ||
function sell(uint256 amount) external; | ||
function claim(uint256 amount) external; | ||
function claimAll() external; | ||
} |