11//SPDX-License-Identifier: Unlicense
22pragma solidity ^ 0.8.0 ;
33
4- import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol " ;
4+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol " ;
5+ import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol " ;
56import {ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol " ;
67
78import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol " ;
89import {AddressQueue} from "./utils/AddressQueue.sol " ;
9- import {BondMinterHelpers} from "./utils/BondMinterHelpers.sol " ;
1010
1111import {IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
12- import {ITranche} from "./interfaces/ITranche.sol " ;
12+ import {ITranche} from "./interfaces/button-wood/ITranche.sol " ;
13+ import {IBondController} from "./interfaces/button-wood/IBondController.sol " ;
1314import {IBondMinter} from "./interfaces/IBondMinter.sol " ;
14- import {IBondController} from "./interfaces/IBondController.sol " ;
15+ import {IFeeStrategy} from "./interfaces/IFeeStrategy.sol " ;
16+ import {IYieldStrategy} from "./interfaces/IYieldStrategy.sol " ;
1517
1618// TODO:
17- // 1) Factor fee params and math into external strategy pattern to enable more complex logic in future
18- // 2) Implement replaceable fee strategies
19- // 3) log events
20- contract ACash is ERC20 , Ownable {
19+ // 1) log events
20+ contract ACash is ERC20 , Initializable , Ownable {
2121 using AddressQueue for AddressQueue.Queue;
22- using BondMinterHelpers for IBondMinter;
2322 using SafeERC20 for IERC20 ;
2423 using SafeERC20 for ITranche;
2524
26- // Used for fee and yield values
27- uint256 public constant PCT_DECIMALS = 6 ;
28-
29- //--- fee strategy parameters
30- // todo: add setter
31- // todo: rethink AMPL fee token, it can rebase up and down, alternatively SPOT as fee?
32- address public feeToken;
33- // Special note: If mint or burn fee is negative, the other must overcompensate in the positive direction.
34- // Otherwise, user could extract from fee reserve by constant mint/burn transactions.
35- int256 public mintFeePct;
36- int256 public burnFeePct;
37- int256 public rolloverRewardPct;
38-
39- //---- bond minter parameters
25+ // minter stores a preset bond config and frequency and mints new bonds when poked
4026 IBondMinter public bondMinter;
41- uint256 public bondMinterConfigIDX;
42- mapping (IBondMinter => uint256 []) trancheYields;
4327
28+ // calculates fees
29+ IFeeStrategy public feeStrategy;
30+
31+ // calculates tranche yields
32+ IYieldStrategy public yieldStrategy;
4433
45- //---- bond queue parameters
4634 // bondQueue is a queue of Bonds, which have an associated number of seniority-based tranches.
4735 AddressQueue.Queue public bondQueue;
4836
@@ -52,20 +40,26 @@ contract ACash is ERC20, Ownable {
5240 //---- ERC-20 parameters
5341 uint8 private immutable _decimals;
5442
55-
5643 // trancheIcebox is a holding area for tranches that are underwater or tranches which are about to mature.
5744 // They can only be rolled over and not burnt
5845 mapping (ITranche => bool ) trancheIcebox;
5946
60- constructor (
61- string memory name ,
62- string memory symbol ,
63- uint8 decimals_ ,
64- IBondMinter bondMinter_ ,
65- uint256 bondMinterConfigIDX_ ,
66- uint256 [] memory bondTrancheYields ) ERC20 (name, symbol) {
47+ constructor (string memory name , string memory symbol , uint8 decimals_ ) ERC20 (name, symbol) {
6748 _decimals = decimals_;
68- setBondMinter (bondMinter_, bondMinterConfigIDX_, bondTrancheYields);
49+ }
50+
51+ function init (
52+ IBondMinter bondMinter_ ,
53+ IYieldStrategy yieldStrategy_ ,
54+ IFeeStrategy feeStrategy_
55+ ) public initializer {
56+ require (address (bondMinter_) != address (0 ), "Expected new bond minter to be valid " );
57+ require (address (yieldStrategy_) != address (0 ), "Expected new yield strategy to be valid " );
58+ require (address (feeStrategy_) != address (0 ), "Expected new fee strategy to be valid " );
59+
60+ bondMinter = bondMinter_;
61+ yieldStrategy = yieldStrategy_;
62+ feeStrategy = feeStrategy_;
6963
7064 bondQueue.init ();
7165 }
@@ -85,49 +79,59 @@ contract ACash is ERC20, Ownable {
8579 uint256 trancheCount = mintingBond.trancheCount ();
8680 require (trancheAmts.length == trancheCount, "Must specify amounts for every bond tranche " );
8781
88- uint256 [] storage yields = trancheYields[bondMinter];
89- // "System Error: trancheYields size doesn't match bond tranche count."
90- // assert(yields.length == trancheCount);
91-
92- uint256 mintAmt = 0 ;
82+ uint256 totalMintAmt = 0 ;
9383 for (uint256 i = 0 ; i < trancheCount; i++ ) {
94- mintAmt += yields[i] * trancheAmts[i] / (10 ** PCT_DECIMALS);
95-
9684 (ITranche t , ) = mintingBond.tranches (i);
97- t.safeTransferFrom (msg .sender , address (this ), trancheAmts[i]);
85+ t.safeTransferFrom (_msgSender (), address (this ), trancheAmts[i]);
86+
87+ totalMintAmt += yieldStrategy.computeTrancheYield (bondMinter, mintingBond, i, trancheAmts[i]);
9888 }
9989
100- // transfer in fee
101- int256 fee = mintFeePct * int256 (mintAmt) / int256 (10 ** PCT_DECIMALS);
102- if (fee >= 0 ) {
103- IERC20 (feeToken).safeTransferFrom (msg .sender , address (this ), uint256 (fee));
90+ // using SPOT as the fee token
91+ int256 fee = feeStrategy.computeMintFee (totalMintAmt);
92+ uint256 mintAmt = 0 ;
93+ if (fee >= 0 ) {
94+ mintAmt = totalMintAmt - uint256 (fee);
95+ _mint (address (this ), uint256 (fee));
10496 } else {
105- // This is very scary!
106- IERC20 (feeToken).safeTransfer (msg .sender , uint256 (- fee));
97+ mintAmt = totalMintAmt + uint256 (- fee);
10798 }
108-
109- // mint spot for user
110- _mint (msg .sender , mintAmt);
111-
112- return (mintAmt, fee);
99+ _mint (_msgSender (), mintAmt);
100+
101+ return (mintAmt, fee);
102+
103+ // transfer in fee in non native fee token token
104+ // int256 fee = feeStrategy.computeMintFee(mintAmt);
105+ // IERC20 feeToken = feeStrategy.feeToken();
106+ // if (fee >= 0) {
107+ // feeToken.safeTransferFrom(_msgSender(), address(this), uint256(fee));
108+ // } else {
109+ // // This is very scary!
110+ // feeToken.safeTransfer(_msgSender(), uint256(-fee));
111+ // }
112+ // return (mintAmt, fee);
113113 }
114114
115115
116116 // push new bond into the queue
117- function advanceMintBond (IBondController newBond ) public onlyOwner {
118- // checks
119- require (bondMinter.isConfigMatch (bondMinterConfigIDX, newBond), "Expect new bond config to match minter config " );
117+ function advanceMintBond (IBondController newBond ) public {
118+ require ( address (newBond) != bondQueue. head (), " New bond already in queue " );
119+ require (bondMinter.isInstance ( address ( newBond)) , "Expect new bond to be minted by the minter " );
120120 require (newBond.maturityDate () > tolarableBondMaturiyDate (), "New bond matures too soon " );
121121
122- // enqueue empty bond, now minters can use this bond to mint!
123122 bondQueue.enqueue (address (newBond));
124123 }
125124
126- // todo: make this iterative to continue dequeue till the tail of the queue
125+ // continue dequeue till the tail of the queue
127126 // has a bond which expires sufficiently out into the future
128- function advanceBurnBond () public onlyOwner {
129- IBondController latestBond = IBondController (bondQueue.tail ());
130- if (address (latestBond) != address (0 ) && latestBond.maturityDate () <= tolarableBondMaturiyDate ()) {
127+ function advanceBurnBond () public {
128+ while (true ){
129+ IBondController latestBond = IBondController (bondQueue.tail ());
130+
131+ if (address (latestBond) == address (0 ) || latestBond.maturityDate () > tolarableBondMaturiyDate ()) {
132+ break ;
133+ }
134+
131135 // pop from queue
132136 bondQueue.dequeue ();
133137
@@ -141,45 +145,42 @@ contract ACash is ERC20, Ownable {
141145 }
142146 }
143147
144- function setBondMinter (IBondMinter bondMinter_ , uint256 bondMinterConfigIDX_ , uint256 [] memory bondTrancheYields ) public onlyOwner {
145- // TODO: consider using custom minter rather than button's
146- // the current version does not have a instance check function
147- require (address (bondMinter_) != address (0 ), "Expected bond minter to be set " );
148-
149- require (bondMinter_.numConfigs () > bondMinterConfigIDX_, "Expected bond minter to be configured " );
150-
148+ function setBondMinter (IBondMinter bondMinter_ ) external onlyOwner {
149+ require (address (bondMinter_) != address (0 ), "Expected new bond minter to be valid " );
151150 bondMinter = bondMinter_;
152- bondMinterConfigIDX = bondMinterConfigIDX_;
153-
154- require (bondTrancheYields.length == bondMinter_.trancheCount (bondMinterConfigIDX_), "Must specify yields for every bond tranche " );
155- trancheYields[bondMinter_] = bondTrancheYields;
156151 }
157152
158- function tolarableBondMaturiyDate () public view returns (uint256 ) {
159- return block .timestamp + _tolarableBondMaturiy;
153+ function setYieldStrategy (IYieldStrategy yieldStrategy_ ) external onlyOwner {
154+ require (address (yieldStrategy_) != address (0 ), "Expected new yield strategy to be valid " );
155+ yieldStrategy = yieldStrategy_;
160156 }
161157
162- /*
163-
164-
165- function calcMintFee(uint256[] calldata trancheAmts) view returns (uint256) {
166-
158+ function setFeeStrategy (IFeeStrategy feeStrategy_ ) external onlyOwner {
159+ require (address (feeStrategy_) != address (0 ), "Expected new fee strategy to be valid " );
160+ feeStrategy = feeStrategy_;
167161 }
168162
163+ function setTolarableBondMaturiy (uint256 tolarableBondMaturiy ) external onlyOwner {
164+ _tolarableBondMaturiy = tolarableBondMaturiy;
165+ }
169166
170- function redeem(uint256 spotAmt) public returns () {
171167
168+ function tolarableBondMaturiyDate () public view returns (uint256 ) {
169+ return block .timestamp + _tolarableBondMaturiy;
172170 }
173171
174- function redeemIcebox(address bond, uint256 trancheAmts) returns () {
172+ /*
173+ function redeem(uint256 spotAmt) public returns () {
175174
176- }
175+ }
177176
178- function rollover() public returns () {
177+ function redeemIcebox(address bond, uint256 trancheAmts) returns () {
179178
180- }
179+ }
181180
181+ function rollover() public returns () {
182182
183+ }
183184 */
184185
185186}
0 commit comments