Skip to content

Commit

Permalink
added helper to compute redeemable tranche balances
Browse files Browse the repository at this point in the history
  • Loading branch information
aalavandhan committed Feb 23, 2023
1 parent e4884a0 commit 535309d
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 168 deletions.
32 changes: 19 additions & 13 deletions spot-contracts/contracts/_utils/BondHelpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -191,27 +191,33 @@ library BondHelpers {
return (td, collateralBalances, trancheSupplies);
}

/// @notice Given a bond, retrieves the collateral redeemable for
/// each tranche held by the given address.
/// @notice Given a bond and a user address computes the tranche amounts proportional to the tranche ratios,
// that can be redeemed for the collateral before maturity.
/// @param b The address of the bond contract.
/// @param u The address to check balance for.
/// @return The tranche data and an array of collateral balances.
function getTrancheCollateralBalances(IBondController b, address u)
/// @return The tranche data and an array of tranche token balances.
function computeRedeemableTrancheAmounts(IBondController b, address u)
internal
view
returns (TrancheData memory, uint256[] memory)
{
TrancheData memory td;
uint256[] memory collateralBalances;
uint256[] memory trancheSupplies;

(td, collateralBalances, trancheSupplies) = getTrancheCollateralizations(b);
TrancheData memory td = getTrancheData(b);
uint256[] memory redeemableAmts = new uint256[](td.trancheCount);

// We calculate the minimum value of {trancheBal/trancheRatio} across tranches
uint256 min = type(uint256).max;
uint8 i;
for (i = 0; i < td.trancheCount && min != 0; i++) {
uint256 d = (td.tranches[i].balanceOf(u) * TRANCHE_RATIO_GRANULARITY) / td.trancheRatios[i];
if (d < min) {
min = d;
}
}

uint256[] memory balances = new uint256[](td.trancheCount);
for (uint8 i = 0; i < td.trancheCount; i++) {
balances[i] = (td.tranches[i].balanceOf(u) * collateralBalances[i]) / trancheSupplies[i];
for (i = 0; i < td.trancheCount; i++) {
redeemableAmts[i] = (td.trancheRatios[i] * min) / TRANCHE_RATIO_GRANULARITY;
}

return (td, balances);
return (td, redeemableAmts);
}
}
16 changes: 8 additions & 8 deletions spot-contracts/contracts/test/BondHelpersTester.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,6 @@ contract BondHelpersTester {
return b.previewDeposit(collateralAmount);
}

function getTrancheCollateralBalances(IBondController b, address u)
public
view
returns (TrancheData memory, uint256[] memory)
{
return b.getTrancheCollateralBalances(u);
}

function getTrancheCollateralizations(IBondController b)
public
view
Expand All @@ -57,4 +49,12 @@ contract BondHelpersTester {
TrancheData memory td = b.getTrancheData();
return td.getTrancheIndex(t);
}

function computeRedeemableTrancheAmounts(IBondController b, address u)
public
view
returns (TrancheData memory td, uint256[] memory)
{
return b.computeRedeemableTrancheAmounts(u);
}
}
148 changes: 1 addition & 147 deletions spot-contracts/test/_utils/BondHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
rebase,
depositIntoBond,
getTrancheBalances,
getTranches,
} from "../helpers";

let bondFactory: Contract,
Expand All @@ -20,18 +19,13 @@ let bondFactory: Contract,
bondHelpers: Contract,
accounts: Signer[],
deployer: Signer,
deployerAddress: string,
user: Signer,
userAddress: string;
deployerAddress: string;

async function setupContracts() {
accounts = await ethers.getSigners();
deployer = accounts[0];
deployerAddress = await deployer.getAddress();

user = accounts[1];
userAddress = await user.getAddress();

bondFactory = await setupBondFactory();
({ collateralToken, rebaseOracle } = await setupCollateralToken("Bitcoin", "BTC"));

Expand Down Expand Up @@ -425,144 +419,4 @@ describe("BondHelpers", function () {
});
});
});

describe("#getTrancheCollateralBalances", function () {
let bond: Contract, bondLength: number;
beforeEach(async function () {
bondLength = 86400;
bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], bondLength);
await depositIntoBond(bond, toFixedPtAmt("1000"), deployer);
const tranches = await getTranches(bond);
await tranches[0].transfer(userAddress, toFixedPtAmt("50"));
await tranches[1].transfer(userAddress, toFixedPtAmt("50"));
await tranches[2].transfer(userAddress, toFixedPtAmt("50"));
});

describe("when bond not mature", function () {
describe("when no change in supply", function () {
it("should calculate the balances", async function () {
const b = await bondHelpers.getTrancheCollateralBalances(bond.address, deployerAddress);
expect(b[1][0]).to.eq(toFixedPtAmt("150"));
expect(b[1][1]).to.eq(toFixedPtAmt("250"));
expect(b[1][2]).to.eq(toFixedPtAmt("450"));

const c = await bondHelpers.getTrancheCollateralBalances(bond.address, userAddress);
expect(c[1][0]).to.eq(toFixedPtAmt("50"));
expect(c[1][1]).to.eq(toFixedPtAmt("50"));
expect(c[1][2]).to.eq(toFixedPtAmt("50"));
});
});

describe("when supply increases above z threshold", function () {
it("should calculate the balances", async function () {
await rebase(collateralToken, rebaseOracle, 0.1);
const b = await bondHelpers.getTrancheCollateralBalances(bond.address, deployerAddress);
expect(b[1][0]).to.eq(toFixedPtAmt("150"));
expect(b[1][1]).to.eq(toFixedPtAmt("250"));
expect(b[1][2]).to.eq(toFixedPtAmt("540"));

const c = await bondHelpers.getTrancheCollateralBalances(bond.address, userAddress);
expect(c[1][0]).to.eq(toFixedPtAmt("50"));
expect(c[1][1]).to.eq(toFixedPtAmt("50"));
expect(c[1][2]).to.eq(toFixedPtAmt("60"));
});
});

describe("when supply decreases below z threshold", function () {
it("should calculate the balances", async function () {
await rebase(collateralToken, rebaseOracle, -0.1);
const b = await bondHelpers.getTrancheCollateralBalances(bond.address, deployerAddress);
expect(b[1][0]).to.eq(toFixedPtAmt("150"));
expect(b[1][1]).to.eq(toFixedPtAmt("250"));
expect(b[1][2]).to.eq(toFixedPtAmt("360"));

const c = await bondHelpers.getTrancheCollateralBalances(bond.address, userAddress);
expect(c[1][0]).to.eq(toFixedPtAmt("50"));
expect(c[1][1]).to.eq(toFixedPtAmt("50"));
expect(c[1][2]).to.eq(toFixedPtAmt("40"));
});
});

describe("when supply decreases below b threshold", function () {
it("should calculate the balances", async function () {
await rebase(collateralToken, rebaseOracle, -0.6);
const b = await bondHelpers.getTrancheCollateralBalances(bond.address, deployerAddress);
expect(b[1][0]).to.eq(toFixedPtAmt("150"));
expect(b[1][1]).to.eq(toFixedPtAmt("166.666666666666666666"));
expect(b[1][2]).to.eq(toFixedPtAmt("0"));

const c = await bondHelpers.getTrancheCollateralBalances(bond.address, userAddress);
expect(c[1][0]).to.eq(toFixedPtAmt("50"));
expect(c[1][1]).to.eq(toFixedPtAmt("33.333333333333333333"));
expect(c[1][2]).to.eq(toFixedPtAmt("0"));
});
});

describe("when supply decreases below a threshold", function () {
it("should calculate the balances", async function () {
await rebase(collateralToken, rebaseOracle, -0.85);
const b = await bondHelpers.getTrancheCollateralBalances(bond.address, deployerAddress);
expect(b[1][0]).to.eq(toFixedPtAmt("112.5"));
expect(b[1][1]).to.eq(toFixedPtAmt("0"));
expect(b[1][2]).to.eq(toFixedPtAmt("0"));

const c = await bondHelpers.getTrancheCollateralBalances(bond.address, userAddress);
expect(c[1][0]).to.eq(toFixedPtAmt("37.5"));
expect(c[1][1]).to.eq(toFixedPtAmt("0"));
expect(c[1][2]).to.eq(toFixedPtAmt("0"));
});
});
});

describe("when bond is mature", function () {
beforeEach(async function () {
await TimeHelpers.increaseTime(bondLength);
await bond.mature(); // NOTE: Any rebase after maturity goes directly to the tranches
});

describe("when no change in supply", function () {
it("should calculate the balances", async function () {
const b = await bondHelpers.getTrancheCollateralBalances(bond.address, deployerAddress);
expect(b[1][0]).to.eq(toFixedPtAmt("150"));
expect(b[1][1]).to.eq(toFixedPtAmt("250"));
expect(b[1][2]).to.eq(toFixedPtAmt("450"));

const c = await bondHelpers.getTrancheCollateralBalances(bond.address, userAddress);
expect(c[1][0]).to.eq(toFixedPtAmt("50"));
expect(c[1][1]).to.eq(toFixedPtAmt("50"));
expect(c[1][2]).to.eq(toFixedPtAmt("50"));
});
});

describe("when supply increases", function () {
it("should calculate the balances", async function () {
await rebase(collateralToken, rebaseOracle, 0.1);
const b = await bondHelpers.getTrancheCollateralBalances(bond.address, deployerAddress);
expect(b[1][0]).to.eq(toFixedPtAmt("165"));
expect(b[1][1]).to.eq(toFixedPtAmt("275"));
expect(b[1][2]).to.eq(toFixedPtAmt("495"));

const c = await bondHelpers.getTrancheCollateralBalances(bond.address, userAddress);
expect(c[1][0]).to.eq(toFixedPtAmt("55"));
expect(c[1][1]).to.eq(toFixedPtAmt("55"));
expect(c[1][2]).to.eq(toFixedPtAmt("55"));
});
});

describe("when supply decreases", function () {
it("should calculate the balances", async function () {
await rebase(collateralToken, rebaseOracle, -0.1);
const b = await bondHelpers.getTrancheCollateralBalances(bond.address, deployerAddress);
expect(b[1][0]).to.eq(toFixedPtAmt("135"));
expect(b[1][1]).to.eq(toFixedPtAmt("225"));
expect(b[1][2]).to.eq(toFixedPtAmt("405"));

const c = await bondHelpers.getTrancheCollateralBalances(bond.address, userAddress);
expect(c[1][0]).to.eq(toFixedPtAmt("45"));
expect(c[1][1]).to.eq(toFixedPtAmt("45"));
expect(c[1][2]).to.eq(toFixedPtAmt("45"));
});
});
});
});
});

0 comments on commit 535309d

Please sign in to comment.