Skip to content

Commit

Permalink
Purely superficial change - reordering functions, tweaking comments, …
Browse files Browse the repository at this point in the history
…etc: zero behavioral impact. (Pushing it now to make the diffs of a big upcoming behavioral change easier to follow...)
  • Loading branch information
jacob-eliosoff committed Feb 3, 2021
1 parent cf6b67a commit ba5431a
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 63 deletions.
6 changes: 3 additions & 3 deletions contracts/IUSM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ abstract contract IUSM {
function latestPrice() public virtual view returns (uint price, uint updateTime);
function latestOraclePrice() public virtual view returns (uint price, uint updateTime);
function ethPool() public virtual view returns (uint pool);
function usmTotalSupply() public virtual view returns (uint supply); // Is there a way to scrap this and just use ERC20(Permit)'s totalSupply()?
function fumTotalSupply() public virtual view returns (uint supply);
function buySellAdjustment() public virtual view returns (uint adjustment);
function ethBuffer(uint ethUsdPrice, uint ethInPool, uint usmSupply, WadMath.Round upOrDown) public virtual pure returns (int buffer);
function debtRatio(uint ethUsdPrice, uint ethInPool, uint usmSupply) public virtual pure returns (uint ratio);
function ethToUsm(uint ethUsdPrice, uint ethAmount, WadMath.Round upOrDown) public virtual pure returns (uint usmOut);
function usmToEth(uint ethUsdPrice, uint usmAmount, WadMath.Round upOrDown) public virtual pure returns (uint ethOut);
function usmTotalSupply() public virtual view returns (uint supply); // Is there a way to scrap this and just use ERC20(Permit)'s totalSupply()?
function fumTotalSupply() public virtual view returns (uint supply);
function usmPrice(Side side, uint ethUsdPrice, uint debtRatio_) public virtual view returns (uint price);
function fumPrice(Side side, uint ethUsdPrice, uint ethInPool, uint usmSupply, uint fumSupply, uint adjustment) public virtual view returns (uint price);
function buySellAdjustment() public virtual view returns (uint adjustment);
}
114 changes: 61 additions & 53 deletions contracts/USM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ contract USM is IUSM, Oracle, ERC20WithOptOut, Delegable {
fum = new FUM(this, optedOut_);
}

/** PUBLIC AND EXTERNAL TRANSACTIONAL FUNCTIONS **/
// ____________________ External transactional functions ____________________

/**
* @notice Mint new USM, sending it to the given address, and only if the amount minted >= minUsmOut. The amount of ETH is
Expand Down Expand Up @@ -126,6 +126,17 @@ contract USM is IUSM, Oracle, ERC20WithOptOut, Delegable {
ethOut = _defundFum(from, to, fumToBurn, minEthOut);
}

/**
* @notice If anyone sends ETH here, assume they intend it as a `mint`.
* If decimals 8 to 11 (included) of the amount of Ether received are `0000` then the next 7 will
* be parsed as the minimum Ether price accepted, with 2 digits before and 5 digits after the comma.
*/
receive() external payable {
_mintUsm(msg.sender, MinOut.parseMinTokenOut(msg.value));
}

// ____________________ Public transactional functions ____________________

function refreshPrice() public virtual override returns (uint price, uint updateTime) {
uint adjustment;
bool priceChanged;
Expand All @@ -136,14 +147,7 @@ contract USM is IUSM, Oracle, ERC20WithOptOut, Delegable {
}
}

/**
* @notice If anyone sends ETH here, assume they intend it as a `mint`.
* If decimals 8 to 11 (included) of the amount of Ether received are `0000` then the next 7 will
* be parsed as the minimum Ether price accepted, with 2 digits before and 5 digits after the comma.
*/
receive() external payable {
_mintUsm(msg.sender, MinOut.parseMinTokenOut(msg.value));
}
// ____________________ Internal ERC20 transactional functions ____________________

/**
* @notice If a user sends USM tokens directly to this contract (or to the FUM contract), assume they intend it as a `burn`.
Expand All @@ -159,7 +163,7 @@ contract USM is IUSM, Oracle, ERC20WithOptOut, Delegable {
return true;
}

/** INTERNAL TRANSACTIONAL FUNCTIONS */
// ____________________ Internal helper transactional functions (for functions above) ____________________

function _mintUsm(address to, uint minUsmOut) internal returns (uint usmOut)
{
Expand Down Expand Up @@ -338,12 +342,14 @@ contract USM is IUSM, Oracle, ERC20WithOptOut, Delegable {
emit BuySellAdjustmentChanged(previous, newAdjustment);
}

/** PUBLIC VIEW FUNCTIONS **/
// ____________________ Public Oracle view functions ____________________

function latestPrice() public virtual override(IUSM, Oracle) view returns (uint price, uint updateTime) {
(price, updateTime) = (storedPrice.value, storedPrice.timestamp);
}

// ____________________ Public informational view functions ____________________

function latestOraclePrice() public virtual override view returns (uint price, uint updateTime) {
(price, updateTime) = oracle.latestPrice();
}
Expand All @@ -356,6 +362,28 @@ contract USM is IUSM, Oracle, ERC20WithOptOut, Delegable {
pool = address(this).balance;
}

function usmTotalSupply() public override view returns (uint supply) {
supply = totalSupply();
}

function fumTotalSupply() public override view returns (uint supply) {
supply = fum.totalSupply();
}

/**
* @notice The current min FUM buy price, equal to the stored value decayed by time since minFumBuyPriceTimestamp.
* @return mfbp The minFumBuyPrice, in ETH terms
*/
function minFumBuyPrice() public view returns (uint mfbp) {
if (storedMinFumBuyPrice.value != 0) {
uint numHalvings = (block.timestamp - storedMinFumBuyPrice.timestamp).wadDivDown(MIN_FUM_BUY_PRICE_HALF_LIFE);
uint decayFactor = numHalvings.wadHalfExp();
mfbp = uint256(storedMinFumBuyPrice.value).wadMulUp(decayFactor);
} // Otherwise just returns 0
}

// ____________________ Public helper pure functions (for functions above) ____________________

/**
* @notice Calculate the amount of ETH in the buffer.
* @return buffer ETH buffer
Expand Down Expand Up @@ -397,14 +425,6 @@ contract USM is IUSM, Oracle, ERC20WithOptOut, Delegable {
ethOut = usmAmount.wadDiv(ethUsdPrice, upOrDown);
}

function usmTotalSupply() public override view returns (uint supply) {
supply = totalSupply();
}

function fumTotalSupply() public override view returns (uint supply) {
supply = fum.totalSupply();
}

/**
* @notice Calculate the *marginal* price of USM (in ETH terms) - that is, of the next unit, before the price start sliding.
* @return price USM price in ETH terms
Expand Down Expand Up @@ -448,6 +468,28 @@ contract USM is IUSM, Oracle, ERC20WithOptOut, Delegable {
}
}

/**
* @notice The current buy/sell adjustment, equal to the stored value decayed by time since buySellAdjustmentTimestamp. This
* adjustment is intended as a measure of "how long-ETH recent user activity has been", so that we can slide price
* accordingly: if recent activity was mostly long-ETH (fund() and burn()), raise FUM buy price/reduce USM sell price; if
* recent activity was short-ETH (defund() and mint()), reduce FUM sell price/raise USM buy price. We use "it reduced debt
* ratio" as a rough proxy for "the operation was long-ETH".
*
* (There is one odd case: when debt ratio > 100%, a *short*-ETH mint() will actually reduce debt ratio. This does no real
* harm except to make fast-succession mint()s and fund()s in such > 100% cases a little more expensive than they would be.)
*
* @return adjustment The sliding-price buy/sell adjustment
*/
function buySellAdjustment() public override view returns (uint adjustment) {
uint numHalvings = (block.timestamp - storedBuySellAdjustment.timestamp).wadDivDown(BUY_SELL_ADJUSTMENT_HALF_LIFE);
uint decayFactor = numHalvings.wadHalfExp(10);
// Here we use the idea that for any b and 0 <= p <= 1, we can crudely approximate b**p by 1 + (b-1)p = 1 + bp - p.
// Eg: 0.6**0.5 pulls 0.6 "about halfway" to 1 (0.8); 0.6**0.25 pulls 0.6 "about 3/4 of the way" to 1 (0.9).
// So b**p =~ b + (1-p)(1-b) = b + 1 - b - p + bp = 1 + bp - p.
// (Don't calculate it as 1 + (b-1)p because we're using uints, b-1 can be negative!)
adjustment = WAD + uint256(storedBuySellAdjustment.value).wadMulDown(decayFactor) - decayFactor;
}

/**
* @notice How much USM a minter currently gets back for ethIn ETH, accounting for adjustment and sliding prices.
* @param ethIn The amount of ETH passed to mint()
Expand Down Expand Up @@ -557,38 +599,4 @@ contract USM is IUSM, Oracle, ERC20WithOptOut, Delegable {
// Using this ending ETH mid price lowerBoundEthUsdPrice1, we can calc the actual average FUM sell price of the defund:
avgFumSellPrice = fumPrice(IUSM.Side.Sell, lowerBoundEthUsdPrice1, ethQty0, usmQty0, fumQty0, adjustment0);
}

/**
* @notice The current min FUM buy price, equal to the stored value decayed by time since minFumBuyPriceTimestamp.
* @return mfbp The minFumBuyPrice, in ETH terms
*/
function minFumBuyPrice() public view returns (uint mfbp) {
if (storedMinFumBuyPrice.value != 0) {
uint numHalvings = (block.timestamp - storedMinFumBuyPrice.timestamp).wadDivDown(MIN_FUM_BUY_PRICE_HALF_LIFE);
uint decayFactor = numHalvings.wadHalfExp();
mfbp = uint256(storedMinFumBuyPrice.value).wadMulUp(decayFactor);
} // Otherwise just returns 0
}

/**
* @notice The current buy/sell adjustment, equal to the stored value decayed by time since buySellAdjustmentTimestamp. This
* adjustment is intended as a measure of "how long-ETH recent user activity has been", so that we can slide price
* accordingly: if recent activity was mostly long-ETH (fund() and burn()), raise FUM buy price/reduce USM sell price; if
* recent activity was short-ETH (defund() and mint()), reduce FUM sell price/raise USM buy price. We use "it reduced debt
* ratio" as a rough proxy for "the operation was long-ETH".
*
* (There is one odd case: when debt ratio > 100%, a *short*-ETH mint() will actually reduce debt ratio. This does no real
* harm except to make fast-succession mint()s and fund()s in such > 100% cases a little more expensive than they would be.)
*
* @return adjustment The sliding-price buy/sell adjustment
*/
function buySellAdjustment() public override view returns (uint adjustment) {
uint numHalvings = (block.timestamp - storedBuySellAdjustment.timestamp).wadDivDown(BUY_SELL_ADJUSTMENT_HALF_LIFE);
uint decayFactor = numHalvings.wadHalfExp(10);
// Here we use the idea that for any b and 0 <= p <= 1, we can crudely approximate b**p by 1 + (b-1)p = 1 + bp - p.
// Eg: 0.6**0.5 pulls 0.6 "about halfway" to 1 (0.8); 0.6**0.25 pulls 0.6 "about 3/4 of the way" to 1 (0.9).
// So b**p =~ b + (1-p)(1-b) = b + 1 - b - p + bp = 1 + bp - p.
// (Don't calculate it as 1 + (b-1)p because we're using uints, b-1 can be negative!)
adjustment = WAD + uint256(storedBuySellAdjustment.value).wadMulDown(decayFactor) - decayFactor;
}
}
2 changes: 1 addition & 1 deletion contracts/USMView.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ contract USMView {
usm = usm_;
}

/** EXTERNAL VIEW FUNCTIONS */
// ____________________ External informational view functions ____________________

/**
* @notice Calculate the amount of ETH in the buffer.
Expand Down
12 changes: 6 additions & 6 deletions test/03_USM.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ contract('USM', (accounts) => {
return new BN(BigInt(Math.round(parseFloat(x) * WAD)))
}

/* ____________________ Deployment ____________________ */
// ____________________ Deployment ____________________

describe("mints and burns a static amount", () => {
let oracle, usm, fum, usmView, ethPerFund, ethPerMint, bitOfEth, snapshot, snapshotId
Expand Down Expand Up @@ -149,7 +149,7 @@ contract('USM', (accounts) => {
})
})

/* ____________________ Minting and burning ____________________ */
// ____________________ Minting and burning ____________________

describe("minting and burning", () => {
let MAX_DEBT_RATIO
Expand Down Expand Up @@ -226,7 +226,7 @@ contract('USM', (accounts) => {
usmSellPrice0 = await usmView.usmPrice(sides.SELL)
})

/* ____________________ Minting FUM (aka fund()) ____________________ */
// ____________________ Minting FUM (aka fund()) ____________________

it("calculates log/exp correctly", async () => {
const w = await WadMath.new()
Expand Down Expand Up @@ -413,7 +413,7 @@ contract('USM', (accounts) => {
totalFumSupply.should.be.bignumber.gt(totalFumSupply0)
})

/* ____________________ Minting USM (aka mint()), at sliding price ____________________ */
// ____________________ Minting USM (aka mint()), at sliding price ____________________

describe("with USM minted at sliding price", () => {
let ethPoolM, priceM, debtRatioM, user2FumBalanceM, totalFumSupplyM, buySellAdjM, fumBuyPriceM,
Expand Down Expand Up @@ -530,7 +530,7 @@ contract('USM', (accounts) => {
shouldEqualApprox(totalUsmSupply, targetTotalUsmSupply)
})

/* ____________________ Burning FUM (aka defund()) ____________________ */
// ____________________ Burning FUM (aka defund()) ____________________

it("allows burning FUM", async () => {
const fumToBurn = user2FumBalance0.div(TWO) // defund 50% of the user's FUM
Expand Down Expand Up @@ -698,7 +698,7 @@ contract('USM', (accounts) => {
await expectRevert(usm.defund(user2, user1, oneFum, 0, { from: user2 }), "Debt ratio > max")
})

/* ____________________ Burning USM (aka burn()) ____________________ */
// ____________________ Burning USM (aka burn()) ____________________

it("allows burning USM", async () => {
const usmToBurn = user1UsmBalance0.div(TWO) // defund 50% of the user's USM
Expand Down

0 comments on commit ba5431a

Please sign in to comment.