11//SPDX-License-Identifier: Unlicense
22pragma solidity ^ 0.8.0 ;
33
4- import " hardhat/console .sol " ;
4+ import { Ownable } from " @openzeppelin/contracts/access/Ownable .sol " ;
55import {ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol " ;
66
7- import "./interfaces/IBondController.sol " ;
7+ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol " ;
8+ import {AddressQueue} from "./utils/AddressQueue.sol " ;
9+ import {BondMinterHelpers} from "./utils/BondMinterHelpers.sol " ;
810
9- import "./utils/AddressQueue.sol " ;
11+ import {IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
12+ import {ITranche} from "./interfaces/ITranche.sol " ;
13+ import {IBondMinter} from "./interfaces/IBondMinter.sol " ;
14+ import {IBondController} from "./interfaces/IBondController.sol " ;
1015
1116// TODO:
1217// 1) Factor fee params and math into external strategy pattern to enable more complex logic in future
1318// 2) Implement replaceable fee strategies
14- contract ACash is ERC20 {
19+ // 3) log events
20+ contract ACash is ERC20 , Ownable {
1521 using AddressQueue for AddressQueue.Queue;
16-
17- // todo: add setter
18- address public feeToken;
22+ using BondMinterHelpers for IBondMinter;
23+ using SafeERC20 for IERC20 ;
1924
2025 // Used for fee and yield values
2126 uint256 public constant PCT_DECIMALS = 6 ;
2227
28+ //--- fee strategy parameters
29+ // todo: add setter
30+ // todo: rethink AMPL fee token, it can rebase up and down, alternatively SPOT as fee?
31+ address public feeToken;
2332 // Special note: If mint or burn fee is negative, the other must overcompensate in the positive direction.
2433 // Otherwise, user could extract from fee reserve by constant mint/burn transactions.
2534 int256 public mintFeePct;
2635 int256 public burnFeePct;
2736 int256 public rolloverRewardPct;
2837
29- address public bondFactory;
30- // bondFactory -> ordered array of tranche yields for SPOT
31- mapping (address => uint256 []) trancheYields;
38+ //---- bond minter parameters
39+ IBondMinter public bondMinter;
40+ uint256 public bondMinterConfigIDX;
41+ mapping (IBondMinter => uint256 []) trancheYields;
3242
33- uint8 private immutable _decimals;
3443
44+ //---- bond queue parameters
3545 // bondQueue is a queue of Bonds, which have an associated number of seniority-based tranches.
3646 AddressQueue.Queue public bondQueue;
3747
38- // bondIcebox is a holding area for tranches that are underwater.
39- // These are skipped in the general burn/redeem case, but may be manually burned redeemed by address
40- mapping (address => bool ) bondIcebox;
41-
42- constructor (string memory name , string memory symbol , uint8 decimals_ ) ERC20 (name, symbol) {
48+ // system only keeps bonds which further than the `tolarableBondMaturiy` in the queue
49+ uint256 private _tolarableBondMaturiy;
50+
51+ //---- ERC-20 parameters
52+ uint8 private immutable _decimals;
53+
54+
55+ // trancheIcebox is a holding area for tranches that are underwater or tranches which are about to mature.
56+ // They can only be rolled over and not burnt
57+ mapping (ITranche => bool ) trancheIcebox;
58+
59+ constructor (
60+ string memory name ,
61+ string memory symbol ,
62+ uint8 decimals_ ,
63+ IBondMinter bondMinter_ ,
64+ uint256 bondMinterConfigIDX_ ,
65+ uint256 [] memory bondTrancheYields ) ERC20 (name, symbol) {
4366 _decimals = decimals_;
67+ setBondMinter (bondMinter_, bondMinterConfigIDX_, bondTrancheYields);
68+
4469 bondQueue.init ();
45- console.log ("Deploying ACash " );
4670 }
4771
72+
4873 function decimals () public view override returns (uint8 ) {
4974 return _decimals;
5075 }
5176
5277 function mint (uint256 [] calldata trancheAmts ) external returns (uint256 , int256 ) {
53- require (bondFactory != address (0 ), "Error: No bond factory set. " );
54-
55- address mintingBond = bondQueue.tail ();
56- require (mintingBond != address (0 ), "Error: No active minting bond " );
57-
58- uint256 trancheCount = IBondController (mintingBond).trancheCount ();
78+ // assert(bondMinter != address(0));
5979
60- require (trancheAmts.length == trancheCount, "Must specify amounts for every Bond Tranche. " );
80+ IBondController mintingBond = IBondController (bondQueue.tail ());
81+ require (address (mintingBond) != address (0 ), "No active minting bond " );
6182
62- uint256 [] storage yields = trancheYields[bondFactory];
83+ uint256 trancheCount = mintingBond.trancheCount ();
84+
85+ require (trancheAmts.length == trancheCount, "Must specify amounts for every bond tranche " );
86+
87+ uint256 [] storage yields = trancheYields[bondMinter];
6388 // "System Error: trancheYields size doesn't match bond tranche count."
6489 assert (yields.length == trancheCount);
6590
6691 uint256 mintAmt = 0 ;
6792 for (uint256 i = 0 ; i < trancheCount; i++ ) {
6893 mintAmt += yields[i] * trancheAmts[i] / (10 ** PCT_DECIMALS);
69- (ITranche t , ) = IBondController ( mintingBond) .tranches (i);
70- IERC20 (t). transferFrom (msg .sender , address (this ), trancheAmts[i]); // assert or use safe transfer
94+ (ITranche t , ) = mintingBond.tranches (i);
95+ IERC20 (address (t)). safeTransferFrom (msg .sender , address (this ), trancheAmts[i]); // assert or use safe transfer
7196 }
7297
7398 // transfer in fee
7499 int256 fee = mintFeePct * int256 (mintAmt) / int256 (10 ** PCT_DECIMALS);
75100 if (fee >= 0 ) {
76- IERC20 (feeToken).transferFrom (msg .sender , address (this ), uint256 (fee));// todo: safe versions
101+ IERC20 (feeToken).safeTransferFrom (msg .sender , address (this ), uint256 (fee)); // todo: safe versions
77102 } else {
78103 // This is very scary!
79- IERC20 (feeToken).transfer (msg .sender , uint256 (- fee));
104+ IERC20 (feeToken).safeTransfer (msg .sender , uint256 (- fee));
80105 }
81-
106+
82107 // mint spot for user
83108 _mint (msg .sender , mintAmt);
84109
85110 return (mintAmt, fee);
86111 }
87112
113+
114+ // push new bond into the queue
115+ function advanceMintBond (IBondController newBond ) public onlyOwner {
116+ // checks
117+ require (bondMinter.isConfigMatch (bondMinterConfigIDX, newBond), "Expect new bond config to match minter config " );
118+ require (newBond.maturityDate () > tolarableBondMaturiyDate (), "New bond matures too soon " );
119+
120+ // enqueue empty bond, now minters can use this bond to mint!
121+ bondQueue.enqueue (address (newBond));
122+ }
123+
124+ // todo: make this iterative to continue dequeue till the tail of the queue
125+ // has a bond which expires sufficiently out into the future
126+ function advanceBurnBond () public onlyOwner {
127+ IBondController latestBond = IBondController (bondQueue.tail ());
128+ if (address (latestBond) != address (0 ) && latestBond.maturityDate () <= tolarableBondMaturiyDate ()) {
129+ // pop from queue
130+ bondQueue.dequeue ();
131+
132+ // push individual tranches into icebox
133+ for (uint256 i = 0 ; i < latestBond.trancheCount (); i++ ){
134+ (ITranche t ,) = latestBond.tranches (i);
135+ trancheIcebox[t] = true ;
136+ }
137+ }
138+ }
139+
140+ function setBondMinter (IBondMinter bondMinter_ , uint256 bondMinterConfigIDX_ , uint256 [] memory bondTrancheYields ) public onlyOwner {
141+ // TODO: consider using custom minter rather than button's
142+ // the current version does not have a instance check function
143+ require (address (bondMinter_) != address (0 ), "Expected bond minter to be set " );
144+
145+ require (bondMinter_.numConfigs () > bondMinterConfigIDX_, "Expected bond minter to be configured " );
146+
147+ bondMinter = bondMinter_;
148+ bondMinterConfigIDX = bondMinterConfigIDX_;
149+
150+ require (bondTrancheYields.length == bondMinter_.trancheCount (bondMinterConfigIDX_), "Must specify yields for every bond tranche " );
151+ trancheYields[bondMinter_] = bondTrancheYields;
152+ }
153+
154+ function tolarableBondMaturiyDate () public view returns (uint256 ) {
155+ return block .timestamp + _tolarableBondMaturiy;
156+ }
157+
88158 /*
159+
160+
89161 function calcMintFee(uint256[] calldata trancheAmts) view returns (uint256) {
90162
91163 }
@@ -103,9 +175,7 @@ contract ACash is ERC20 {
103175
104176 }
105177
106- function advanceBond(address bond) public onlyOwner {
107- // enqueue empty bond
108- }
178+
109179 */
110180
111181}
0 commit comments