Skip to content

Commit 7727a1b

Browse files
committed
Merge branch 'master' into confirmation-script
2 parents 235c703 + 946d9b1 commit 7727a1b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1512
-246
lines changed

.solcover.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,11 @@ module.exports = {
1515
'plugins/aave',
1616
'fuzz',
1717
],
18+
configureYulOptimizer: true,
19+
solcOptimizerDetails: {
20+
yul: true,
21+
yulDetails: {
22+
stackAllocation: true,
23+
}
24+
},
1825
}

1-RTKN-tmp-deployments.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"facadeWrite": "0xF7bd1F8FdE9fBdc8436D45594e792e014c5ac966",
3+
"main": "0x80403bB991A76908Daea5A4330aEe5B4073F96c4",
4+
"components": {
5+
"assetRegistry": "0x028de53295eB1dAE2Ec5cad3170aE2099DcdD481",
6+
"backingManager": "0xB633e4c5cbdeCE9F7DfcD313bfE68B123c27E2b4",
7+
"basketHandler": "0xA11f1Ad3131d07aB47D979a0B2786F73d8E75f44",
8+
"broker": "0xA78d4a43C7C3c0f4bA5DF46383f80045aCBa859B",
9+
"distributor": "0xf1B3c930488f681aE5Be541E62F51071e0f393ba",
10+
"furnace": "0x5bCCD40e34A89004D0dfC08567AEea1D08254ca1",
11+
"rsrTrader": "0x48C96a5da5ba09Ae110cFEF5f5B6f01e38f5A283",
12+
"rTokenTrader": "0x332590FE351ac5a0cDC3a925D96970508Bb4F5c1",
13+
"rToken": "0xAf48c99b3b4E47286dac9E53E091b70536CB4373",
14+
"stRSR": "0xa095D71e089ec94c65c5248E76380103A5d1461c"
15+
},
16+
"rTokenAsset": "0xE91f031c02Df59a286A185E2209cDE4a8ECD5e49",
17+
"governance": "0x24EDE1dc24462f70E2BA1B79cE815A025F0e0921",
18+
"timelock": "0xBa6d841e1A9C1C5A034f4C79c02dd503bacfc67A"
19+
}

1-tmp-assets-collateral.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"assets": {
3+
"stkAAVE": "0x1895534f4D0fF3E632407DB5327cE3eAfCe2A9f6",
4+
"COMP": "0xd9e49644f35b5233B5191b14882242bd0bdF58E8"
5+
},
6+
"collateral": {
7+
"DAI": "0xe5E6bBE251c22C9E100a3A10e88C5Abdfd24f6d8",
8+
"USDC": "0xb241baed74a8b2199D4d516F20Ec529FBb32F3c7",
9+
"USDT": "0xd73F2858A7Bf1d5Bc7523670c14F5EF4b7E37A54",
10+
"USDP": "0x45C515e8E7cB1543364303cfB1dDBA9B0Ad13de9",
11+
"TUSD": "0x0023b264bDD45Dd95B12198A659109Edc3C8b9Bd",
12+
"BUSD": "0x22594aD1C779732E6D015712478445A68cC39fe4",
13+
"aDAI": "0x533BFe91fd2Db80A331e4FD815b19D052cbBA0Bf",
14+
"aUSDC": "0xD995836dA78F416C4bD60Edd7EC1282Ec08a7e0C",
15+
"aUSDT": "0x75fAC74B93F9e919493cfb77099572AacAa67BD7",
16+
"aBUSD": "0x5f933959a92170B46AF82053d1Af289709f6A6f1",
17+
"cDAI": "0x52160CA2651CBEFCc6b603828290A67f80C14764",
18+
"cUSDC": "0xA5CBDe1A0FeB3112bBbe421226a24EF806A44a30",
19+
"cUSDT": "0x94494d7C044Eb8847701C6e96B15d8FF78667623",
20+
"cWBTC": "0x6C44A7270FCEd64Ca0A9A8c4DB998A8b218e9301",
21+
"cETH": "0x1A406077d4BA972e2d879aA62D00BF553CdF6621",
22+
"WBTC": "0xC301E3299b2014f9Db052582978e7282f2c3119a",
23+
"WETH": "0x7d61D0d965Db9ED75eb170742259c6F40AF31605",
24+
"EURT": "0x650C62eC7aC3DB6d08fd922Cc859183Cd79903E7"
25+
}
26+
}

1-tmp-deployments.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"prerequisites": {
3+
"RSR": "0x320623b8e4ff03373931769a31fc52a4e78b5d70",
4+
"RSR_FEED": "0x759bBC1be8F90eE6457C44abc7d443842a976d02",
5+
"GNOSIS_EASY_AUCTION": "0x0b7fFc1f4AD541A4Ed16b40D8c37f0929158D101"
6+
},
7+
"rewardableLib": "0xC84ba848994FF852445a72F065eb4F4ad268cDfB",
8+
"tradingLib": "0xEAb55f04aE29eC47Ec8E3985b74e4b3831d6b69b",
9+
"permitLib": "0x434A17661a52F83FF9E819447E6bfC1166f419Fc",
10+
"oracleLib": "0xA728Ad6a58ed73671A2aaD23512D8AAEe6837a36",
11+
"facade": "0x3DAf5a7681a9cfB92fB38983EB3998dFC7963B28",
12+
"facadeWriteLib": "0x6BAe2026974913867352971bf07917E7Fe29CeEC",
13+
"facadeWrite": "0xF7bd1F8FdE9fBdc8436D45594e792e014c5ac966",
14+
"deployer": "0x9cAc8ED3297040626D8aA6317F5e29813A6A8fc6",
15+
"rsrAsset": "0xC4de1a39d129C3EE2277043B16623A29c7AbC5e1",
16+
"implementations": {
17+
"main": "0xc7333E4DCb11e97835E5Eec96A925946DEACb2F0",
18+
"trade": "0x1631C48fa22007f6F2F39BF15CA5f5bd04C2E3fD",
19+
"components": {
20+
"assetRegistry": "0xF8f435F9998f7E04F8ECA73A09d73005d3Ca7f29",
21+
"backingManager": "0xD82f93E193526975d4783604dC9490B916Dd0b0C",
22+
"basketHandler": "0xCb265acbB3926b76D1546297280563AC6ce58942",
23+
"broker": "0x70d6A435614636797baE4aFCcf8c49f6835Af23c",
24+
"distributor": "0x8211470E739C3734bF7Ccc9cEF03A14597E881f7",
25+
"furnace": "0x2a128Dc88b890688B46534046F9e7AA800b0c0D8",
26+
"rsrTrader": "0x6611E637E02701Ec4dAE6b9b37904a798D2541C2",
27+
"rTokenTrader": "0x6611E637E02701Ec4dAE6b9b37904a798D2541C2",
28+
"rToken": "0x4EA9C4da254b9cBaFaCeD197D3f5E094E848Ba1a",
29+
"stRSR": "0x7af9Cd3D081ff3Df92C19294a6e8056262dd7c93"
30+
}
31+
}
32+
}

contracts/p0/BasketHandler.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ struct BackupConfig {
3232
// if name is in values(targetNames), then backups[name] is a valid BackupConfig
3333
// erc20s is a valid collateral array
3434
//
35-
// TODO: keys(targetAmts) can be a strict superset of erc20s.
36-
// Ticket: https://app.asana.com/0/1202557536393044/1203043664234027/f
3735
// In the meantime, treat erc20s as the canonical set of keys for the target* maps
3836
struct BasketConfig {
3937
// The collateral erc20s in the prime (explicitly governance-set) basket

contracts/p0/mixins/TradingLib.sol

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ library TradingLibP0 {
106106

107107
if (address(surplus) == address(0) || address(deficit) == address(0)) return (false, req);
108108

109-
// TODO make nextTradePair return a struct that contains these prices too
110109
uint192 sellPrice = surplus.strictPrice(); // {UoA/tok}
111110
uint192 buyPrice = deficit.strictPrice(); // {UoA/tok}
112111
assert(buyPrice > 0);
@@ -148,8 +147,6 @@ library TradingLibP0 {
148147
/// The plausible range of BUs that the BackingManager will own after recollateralization.
149148
/// @param erc20s Assets this computation presumes may be traded to raise funds.
150149
//
151-
// TODO: what if erc20s does not contain all basket collateral?
152-
//
153150
// This function returns a "plausible range of BUs" assuming that the trading process follows
154151
// the follwing rules:
155152
//
@@ -163,12 +160,19 @@ library TradingLibP0 {
163160
// - The worst price we might get for an UNPRICED or DISABLED collateral is 0.
164161
// - Given all that, we're aiming to hold as many BUs as possible using the assets we own.
165162
//
166-
// Given these assumptions
167-
// range.top = min(rToken(trader).basketsNeeded, totalAssetValue(erc20s) / basket.price())
168-
// because (totalAssetValue(erc20s) / basket.price()) is how many BUs we can hold assuming
163+
// Given these assumptions, the following hold:
164+
//
165+
// range.top = min(rToken.basketsNeeded, totalAssetValue(erc20s).high / basket.price())
166+
// because (totalAssetValue(erc20s).high / basket.price()) is how many BUs we can hold given
169167
// "best plausible" prices, and we won't try to hold more than rToken(trader).basketsNeeded
170-
// range.bottom = TODO
171-
168+
//
169+
// range.bottom = max(0, min(pessimisticBUs, range.top)), where:
170+
// pessimisticBUs = (assetsLow - maxTradeSlippage * buShortfall(range.top)) / basket.price()
171+
// is the number of BUs that we are *sure* we have the assets to collateralize
172+
// (making the above assumptions about actual trade prices), and
173+
// buShortfall(range.top) = the total value of the assets we'd need to buy in order
174+
// in order to fully collataeralize `range.top` BUs,
175+
//
172176
function basketRange(ITrading trader, IERC20[] memory erc20s)
173177
internal
174178
view
@@ -393,11 +397,6 @@ library TradingLibP0 {
393397
maxes.surplusStatus = CollateralStatus.IFFY; // least-desirable sell status
394398
uint192 minTradeVolume_ = trader.minTradeVolume();
395399

396-
// TODO
397-
// We can do _much much_ better in terms of gas usage than we currently are, but for now
398-
// this function is in its highly unoptimized form so we can confirm overall
399-
// proper behavior
400-
401400
for (uint256 i = 0; i < erc20s.length; ++i) {
402401
if (erc20s[i] == rsr(trader)) continue;
403402

contracts/p1/AssetRegistry.sol

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import "contracts/p1/mixins/Component.sol";
1212
contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
1313
using EnumerableSet for EnumerableSet.AddressSet;
1414

15+
// Peer-component addresses
16+
IBasketHandler private basketHandler;
17+
1518
// Registered ERC20s
1619
EnumerableSet.AddressSet private _erc20s;
1720

@@ -30,6 +33,7 @@ contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
3033
// effects: assets' = {a.erc20(): a for a in assets_}
3134
function init(IMain main_, IAsset[] calldata assets_) external initializer {
3235
__Component_init(main_);
36+
basketHandler = main_.basketHandler();
3337
uint256 length = assets_.length;
3438
for (uint256 i = 0; i < length; ++i) {
3539
_register(assets_[i]);
@@ -70,11 +74,11 @@ contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
7074
function swapRegistered(IAsset asset) external governance returns (bool swapped) {
7175
require(_erc20s.contains(address(asset.erc20())), "no ERC20 collision");
7276

73-
uint192 quantity = main.basketHandler().quantity(asset.erc20());
77+
uint192 quantity = basketHandler.quantity(asset.erc20());
7478

7579
swapped = _registerIgnoringCollisions(asset);
7680

77-
if (quantity > 0) main.basketHandler().disableBasket();
81+
if (quantity > 0) basketHandler.disableBasket();
7882
}
7983

8084
/// Unregister an asset, requiring that it is already registered
@@ -84,13 +88,13 @@ contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
8488
function unregister(IAsset asset) external governance {
8589
require(_erc20s.contains(address(asset.erc20())), "no asset to unregister");
8690
require(assets[asset.erc20()] == asset, "asset not found");
87-
uint192 quantity = main.basketHandler().quantity(asset.erc20());
91+
uint192 quantity = basketHandler.quantity(asset.erc20());
8892

8993
_erc20s.remove(address(asset.erc20()));
9094
assets[asset.erc20()] = IAsset(address(0));
9195
emit AssetUnregistered(asset.erc20(), asset);
9296

93-
if (quantity > 0) main.basketHandler().disableBasket();
97+
if (quantity > 0) basketHandler.disableBasket();
9498
}
9599

96100
/// Return the Asset registered for erc20; revert if erc20 is not registered.
@@ -163,5 +167,5 @@ contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
163167
* variables without shifting down storage in the inheritance chain.
164168
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
165169
*/
166-
uint256[48] private __gap;
170+
uint256[47] private __gap;
167171
}

contracts/p1/BackingManager.sol

Lines changed: 46 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ contract BackingManagerP1 is TradingP1, IBackingManager {
2121
using FixLib for uint192;
2222
using SafeERC20Upgradeable for IERC20Upgradeable;
2323

24+
// Cache of peer components
25+
IAssetRegistry private assetRegistry;
26+
IBasketHandler private basketHandler;
27+
IDistributor private distributor;
28+
IRToken private rToken;
29+
IERC20 private rsr;
30+
IStRSR private stRSR;
31+
IRevenueTrader private rsrTrader;
32+
IRevenueTrader private rTokenTrader;
2433
uint48 public constant MAX_TRADING_DELAY = 31536000; // {s} 1 year
2534
uint192 public constant MAX_BACKING_BUFFER = 1e18; // {%}
2635

@@ -40,7 +49,17 @@ contract BackingManagerP1 is TradingP1, IBackingManager {
4049
uint192 minTradeVolume_
4150
) external initializer {
4251
__Component_init(main_);
43-
__Trading_init(maxTradeSlippage_, minTradeVolume_);
52+
__Trading_init(main_, maxTradeSlippage_, minTradeVolume_);
53+
54+
assetRegistry = main_.assetRegistry();
55+
basketHandler = main_.basketHandler();
56+
distributor = main_.distributor();
57+
rsr = main_.rsr();
58+
rsrTrader = main_.rsrTrader();
59+
rTokenTrader = main_.rTokenTrader();
60+
rToken = main_.rToken();
61+
stRSR = main_.stRSR();
62+
4463
setTradingDelay(tradingDelay_);
4564
setBackingBuffer(backingBuffer_);
4665
}
@@ -50,11 +69,11 @@ contract BackingManagerP1 is TradingP1, IBackingManager {
5069
// checks: erc20 in assetRegistry
5170
// action: set allowance on erc20 for rToken to UINT_MAX
5271
function grantRTokenAllowance(IERC20 erc20) external notPausedOrFrozen {
53-
require(main.assetRegistry().isRegistered(erc20), "erc20 unregistered");
72+
require(assetRegistry.isRegistered(erc20), "erc20 unregistered");
5473
// == Interaction ==
55-
uint256 currAllowance = erc20.allowance(address(this), address(main.rToken()));
74+
uint256 currAllowance = erc20.allowance(address(this), address(rToken));
5675
IERC20Upgradeable(address(erc20)).safeIncreaseAllowance(
57-
address(main.rToken()),
76+
address(rToken),
5877
type(uint256).max - currAllowance
5978
);
6079
}
@@ -86,24 +105,21 @@ contract BackingManagerP1 is TradingP1, IBackingManager {
86105
// only called internally, from manageTokens*, so erc20s has no duplicates unique
87106
// (but not necessarily all registered or valid!)
88107
function _manageTokens(IERC20[] calldata erc20s) private {
89-
IBasketHandler bh = main.basketHandler();
90-
91108
// == Refresh ==
92-
main.assetRegistry().refresh();
109+
assetRegistry.refresh();
93110

94111
if (tradesOpen > 0) return;
95112
// Only trade when all the collateral assets in the basket are SOUND
96-
require(bh.status() == CollateralStatus.SOUND, "basket not sound");
113+
require(basketHandler.status() == CollateralStatus.SOUND, "basket not sound");
97114

98-
uint48 basketTimestamp = bh.timestamp();
115+
uint48 basketTimestamp = basketHandler.timestamp();
99116
if (block.timestamp < basketTimestamp + tradingDelay) return;
100117

101-
if (bh.fullyCollateralized()) {
118+
if (basketHandler.fullyCollateralized()) {
102119
// == Interaction (then return) ==
103120
handoutExcessAssets(erc20s);
104121
} else {
105-
/*
106-
* Recollateralization
122+
/* Recollateralization
107123
*
108124
* Strategy: iteratively move the system on a forgiving path towards capitalization
109125
* through a narrowing BU price band. The initial large spread reflects the
@@ -114,19 +130,16 @@ contract BackingManagerP1 is TradingP1, IBackingManager {
114130
*
115131
* If we run out of capital and are still undercapitalized, we compromise
116132
* rToken.basketsNeeded to the current basket holdings. Haircut time.
117-
*
118-
* TODO: Document our arguments for this procedure.
119-
* See: https://app.asana.com/0/1202557536393044/1203043664234023/f
120133
*/
121134

122135
(bool doTrade, TradeRequest memory req) = RecollateralizationLibP1
123136
.prepareRecollateralizationTrade(this);
124137

125138
if (doTrade) {
126139
// Seize RSR if needed
127-
if (req.sell.erc20() == main.rsr()) {
140+
if (req.sell.erc20() == rsr) {
128141
uint256 bal = req.sell.erc20().balanceOf(address(this));
129-
if (req.sellAmount > bal) main.stRSR().seizeRSR(req.sellAmount - bal);
142+
if (req.sellAmount > bal) stRSR.seizeRSR(req.sellAmount - bal);
130143
}
131144

132145
tryTrade(req);
@@ -157,28 +170,24 @@ contract BackingManagerP1 is TradingP1, IBackingManager {
157170
* RSR and RToken traders according to the distribution totals.
158171
*/
159172

160-
IBasketHandler basketHandler = main.basketHandler();
161-
address rsrTrader = address(main.rsrTrader());
162-
address rTokenTrader = address(main.rTokenTrader());
163-
164173
// Forward any RSR held to StRSR pool; RSR should never be sold for RToken yield
165-
if (main.rsr().balanceOf(address(this)) > 0) {
174+
if (rsr.balanceOf(address(this)) > 0) {
166175
// For CEI, this is an interaction "within our system" even though RSR is already live
167-
IERC20Upgradeable(address(main.rsr())).safeTransfer(
168-
address(main.rsrTrader()),
169-
main.rsr().balanceOf(address(this))
176+
IERC20Upgradeable(address(rsr)).safeTransfer(
177+
address(rsrTrader),
178+
rsr.balanceOf(address(this))
170179
);
171180
}
172181

173182
// Mint revenue RToken and update `basketsNeeded`
174-
// property across this block (DESIRED TODO):
183+
// across this block:
175184
// where rate(R) == R.basketsNeeded / R.totalSupply,
176-
// rate(rToken') >== rate(rToken)
177-
// (>== is "no less than, and practically equal to")
178-
// ... and rToken'.basketsNeeded <== basketHandler.basketsHeldBy(this)
185+
// rate(rToken') >= rate(rToken)
186+
// (>== is "no less than, and nearly equal to")
187+
// and rToken'.basketsNeeded <= basketHandler.basketsHeldBy(this)
188+
// and rToken'.totalSupply is maximal satisfying this.
179189
uint192 needed; // {BU}
180190
{
181-
IRToken rToken = main.rToken();
182191
needed = rToken.basketsNeeded(); // {BU}
183192
uint192 held = basketHandler.basketsHeldBy(address(this)); // {BU}
184193
if (held.gt(needed)) {
@@ -206,11 +215,11 @@ contract BackingManagerP1 is TradingP1, IBackingManager {
206215

207216
// Handout excess assets above what is needed, including any recently minted RToken
208217
uint256 length = erc20s.length;
209-
RevenueTotals memory totals = main.distributor().totals();
218+
RevenueTotals memory totals = distributor.totals();
210219
uint256[] memory toRSR = new uint256[](length);
211220
uint256[] memory toRToken = new uint256[](length);
212221
for (uint256 i = 0; i < length; ++i) {
213-
IAsset asset = main.assetRegistry().toAsset(erc20s[i]);
222+
IAsset asset = assetRegistry.toAsset(erc20s[i]);
214223

215224
uint192 req = needed.mul(basketHandler.quantity(erc20s[i]), CEIL);
216225
if (asset.bal(address(this)).gt(req)) {
@@ -228,17 +237,17 @@ contract BackingManagerP1 is TradingP1, IBackingManager {
228237
// == Interactions ==
229238
for (uint256 i = 0; i < length; ++i) {
230239
IERC20Upgradeable erc20 = IERC20Upgradeable(address(erc20s[i]));
231-
if (toRToken[i] > 0) erc20.safeTransfer(rTokenTrader, toRToken[i]);
232-
if (toRSR[i] > 0) erc20.safeTransfer(rsrTrader, toRSR[i]);
240+
if (toRToken[i] > 0) erc20.safeTransfer(address(rTokenTrader), toRToken[i]);
241+
if (toRSR[i] > 0) erc20.safeTransfer(address(rsrTrader), toRSR[i]);
233242
}
234243

235244
// It's okay if there is leftover dust for RToken or a surplus asset (not RSR)
236245
}
237246

238247
/// Compromise on how many baskets are needed in order to recollateralize-by-accounting
239248
function compromiseBasketsNeeded() private {
240-
assert(tradesOpen == 0 && !main.basketHandler().fullyCollateralized());
241-
main.rToken().setBasketsNeeded(main.basketHandler().basketsHeldBy(address(this)));
249+
assert(tradesOpen == 0 && !basketHandler.fullyCollateralized());
250+
rToken.setBasketsNeeded(basketHandler.basketsHeldBy(address(this)));
242251
}
243252

244253
// === Governance Setters ===
@@ -262,5 +271,5 @@ contract BackingManagerP1 is TradingP1, IBackingManager {
262271
* variables without shifting down storage in the inheritance chain.
263272
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
264273
*/
265-
uint256[48] private __gap;
274+
uint256[41] private __gap;
266275
}

0 commit comments

Comments
 (0)