Skip to content

Commit

Permalink
fix: roll after maturity
Browse files Browse the repository at this point in the history
  • Loading branch information
alcueca committed May 25, 2021
1 parent 4d79270 commit d54f9eb
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 35 deletions.
69 changes: 35 additions & 34 deletions contracts/Ladle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import "./math/WMul.sol";
import "./math/CastU256U128.sol";
import "./math/CastU128I128.sol";
import "./LadleStorage.sol";
import "hardhat/console.sol";


/// @dev Ladle orchestrates contract calls throughout the Yield Protocol v2 into useful and efficient user oriented features.
Expand Down Expand Up @@ -276,40 +277,6 @@ contract Ladle is LadleStorage, AccessControl() {

// ---- Asset and debt management ----

/// @dev Change series and debt of a vault.
function _roll(bytes12 vaultId, DataTypes.Vault memory vault, bytes6 newSeriesId, uint8 loan, uint128 max)
private
returns (DataTypes.Vault memory, DataTypes.Balances memory)
{
DataTypes.Series memory series = getSeries(vault.seriesId);
DataTypes.Series memory newSeries = getSeries(newSeriesId);
DataTypes.Balances memory balances = cauldron.balances(vaultId);

uint128 newDebt;
{
IPool pool = getPool(newSeriesId);
IFYToken fyToken = IFYToken(newSeries.fyToken);
IJoin baseJoin = getJoin(series.baseId);

// Calculate debt in fyToken terms
uint128 amt = _debtInBase(vault.seriesId, series, balances.art);

// Mint fyToken to the pool, as a kind of flash loan
fyToken.mint(address(pool), amt * loan); // Loan is the size of the flash loan relative to the debt amount, 2 should be safe most of the time

// Buy the base required to pay off the debt in series 1, and find out the debt in series 2
newDebt = pool.buyBase(address(baseJoin), amt, max);
baseJoin.join(address(baseJoin), amt); // Repay the old series debt

pool.retrieveFYToken(address(fyToken)); // Get the surplus fyToken
fyToken.burn(address(fyToken), (amt * loan) - newDebt); // Burn the surplus
}

newDebt += ((series.maturity - block.timestamp) * uint256(newDebt).wmul(borrowingFee)).u128(); // Add borrowing fee

return cauldron.roll(vaultId, newSeriesId, newDebt.i128() - balances.art.i128()); // Change the series and debt for the vault
}

/// @dev Move collateral and debt between vaults.
function _stir(bytes12 from, bytes12 to, uint128 ink, uint128 art)
private
Expand Down Expand Up @@ -432,6 +399,40 @@ contract Ladle is LadleStorage, AccessControl() {
pool.retrieveBase(msg.sender);
}

/// @dev Change series and debt of a vault.
function _roll(bytes12 vaultId, DataTypes.Vault memory vault, bytes6 newSeriesId, uint8 loan, uint128 max)
private
returns (DataTypes.Vault memory, DataTypes.Balances memory)
{
DataTypes.Series memory series = getSeries(vault.seriesId);
DataTypes.Series memory newSeries = getSeries(newSeriesId);
DataTypes.Balances memory balances = cauldron.balances(vaultId);

uint128 newDebt;
{
IPool pool = getPool(newSeriesId);
IFYToken fyToken = IFYToken(newSeries.fyToken);
IJoin baseJoin = getJoin(series.baseId);

// Calculate debt in fyToken terms
uint128 amt = _debtInBase(vault.seriesId, series, balances.art);

// Mint fyToken to the pool, as a kind of flash loan
fyToken.mint(address(pool), amt * loan); // Loan is the size of the flash loan relative to the debt amount, 2 should be safe most of the time

// Buy the base required to pay off the debt in series 1, and find out the debt in series 2
newDebt = pool.buyBase(address(baseJoin), amt, max);
baseJoin.join(address(baseJoin), amt); // Repay the old series debt

pool.retrieveFYToken(address(fyToken)); // Get the surplus fyToken
fyToken.burn(address(fyToken), (amt * loan) - newDebt); // Burn the surplus
}

newDebt += ((newSeries.maturity - block.timestamp) * uint256(newDebt).wmul(borrowingFee)).u128(); // Add borrowing fee, also stops users form rolling to a mature series

return cauldron.roll(vaultId, newSeriesId, newDebt.i128() - balances.art.i128()); // Change the series and debt for the vault
}

/// @dev Remove liquidity in a pool and use proceedings to repay debt
/// The liquidity tokens need to be already in the pool, unaccounted for.
function _removeAndRepay(bytes12 vaultId, DataTypes.Vault memory vault, address to, uint128 minBaseOut, uint128 minFYTokenOut)
Expand Down
19 changes: 18 additions & 1 deletion test/065_ladle_roll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('Ladle - roll', function () {
let other: string
let cauldron: Cauldron
let fyToken: FYToken
let otherFYToken: FYToken
let base: ERC20Mock
let ladle: LadleWrapper
let ladleFromOther: LadleWrapper
Expand Down Expand Up @@ -56,6 +57,7 @@ describe('Ladle - roll', function () {
ladleFromOther = ladle.connect(otherAcc)
base = env.assets.get(baseId) as ERC20Mock
fyToken = env.series.get(seriesId) as FYToken
otherFYToken = env.series.get(otherSeriesId) as FYToken

// ==== Set testing environment ====
await cauldron.build(owner, vaultId, seriesId, ilkId)
Expand All @@ -81,9 +83,24 @@ describe('Ladle - roll', function () {
await ladle.roll(vaultId, otherSeriesId, loan, MAX)
const { timestamp } = await ethers.provider.getBlock('latest')
const preFeeDebt = WAD.mul(105).div(100)
const appliedFee = (await fyToken.maturity()).sub(timestamp).mul(preFeeDebt).mul(fee).div(WAD)
const appliedFee = (await otherFYToken.maturity()).sub(timestamp).mul(preFeeDebt).mul(fee).div(WAD)

expect(await fyToken.balanceOf(owner)).to.equal(WAD)
expect((await cauldron.balances(vaultId)).art).to.equal(preFeeDebt.add(appliedFee))
})

describe('after maturity', async () => {
beforeEach(async () => {
await ethers.provider.send('evm_mine', [(await fyToken.maturity()).toNumber()])
})

it('rolls a vault', async () => {
expect(await ladle.roll(vaultId, otherSeriesId, loan, MAX))
.to.emit(cauldron, 'VaultRolled')
.withArgs(vaultId, otherSeriesId, WAD.mul(105).div(100)) // Mock pools have a constant rate of 5%
expect((await cauldron.vaults(vaultId)).seriesId).to.equal(otherSeriesId)
expect((await cauldron.balances(vaultId)).ink).to.equal(WAD)
expect((await cauldron.balances(vaultId)).art).to.equal(WAD.mul(105).div(100))
})
})
})

0 comments on commit d54f9eb

Please sign in to comment.