Skip to content

Commit

Permalink
Adding current balance of tBTC to depositBalancer var in MezoAllocator
Browse files Browse the repository at this point in the history
Included tbtc.balanceOf(address(this)) to calculate the total assets
held by Mezo Allocator contract. This is to properly calculate all the
assets including any 'donations' that were made directly to Mezo
Allocator contract. As a result, new deposits will receive a correct
amount of shares. Without this fix, new deposits could've received more
shares that intended.
  • Loading branch information
dimpar committed May 2, 2024
1 parent efcb6ed commit bb42bce
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 59 deletions.
5 changes: 3 additions & 2 deletions solidity/contracts/MezoAllocator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,10 @@ contract MezoAllocator is IDispatcher, Ownable2StepUpgradeable {
emit MaintainerRemoved(maintainerToRemove);
}

/// @notice Returns the total amount of tBTC allocated to MezoPortal.
/// @notice Returns the total amount of tBTC allocated to MezoPortal including
/// the amount that is currently hold by this contract.
function totalAssets() external view returns (uint256) {
return depositBalance;
return depositBalance + tbtc.balanceOf(address(this));
}

/// @notice Returns the list of maintainers.
Expand Down
5 changes: 3 additions & 2 deletions solidity/contracts/test/upgrades/MezoAllocatorV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,10 @@ contract MezoAllocatorV2 is IDispatcher, Ownable2StepUpgradeable {
emit MaintainerRemoved(maintainerToRemove);
}

/// @notice Returns the total amount of tBTC allocated to MezoPortal.
/// @notice Returns the total amount of tBTC allocated to MezoPortal including
/// the amount that is currently hold by this contract.
function totalAssets() external view returns (uint256) {
return depositBalance;
return depositBalance + tbtc.balanceOf(address(this));
}

/// @notice Returns the list of maintainers.
Expand Down
204 changes: 149 additions & 55 deletions solidity/test/MezoAllocator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ const { getNamedSigners, getUnnamedSigners } = helpers.signers
async function fixture() {
const { tbtc, stbtc, mezoAllocator, mezoPortal } = await deployment()
const { governance, maintainer } = await getNamedSigners()
const [depositor, thirdParty] = await getUnnamedSigners()
const [depositor, depositor2, thirdParty] = await getUnnamedSigners()

return {
governance,
thirdParty,
depositor,
depositor2,
maintainer,
tbtc,
stbtc,
Expand All @@ -42,13 +43,15 @@ describe("MezoAllocator", () => {

let thirdParty: HardhatEthersSigner
let depositor: HardhatEthersSigner
let depositor2: HardhatEthersSigner
let maintainer: HardhatEthersSigner
let governance: HardhatEthersSigner

before(async () => {
;({
thirdParty,
depositor,
depositor2,
maintainer,
governance,
tbtc,
Expand All @@ -70,84 +73,175 @@ describe("MezoAllocator", () => {
})

context("when the caller is maintainer", () => {
context("when a first deposit is made", () => {
let tx: ContractTransactionResponse
context("when two consecutive deposits are made", () => {
beforeAfterSnapshotWrapper()

before(async () => {
await tbtc.mint(await stbtc.getAddress(), to1e18(6))
tx = await mezoAllocator.connect(maintainer).allocate()
})
context("when a first deposit is made", () => {
let tx: ContractTransactionResponse

it("should deposit and transfer tBTC to Mezo Portal", async () => {
await expect(tx).to.changeTokenBalances(
tbtc,
[await mezoPortal.getAddress()],
[to1e18(6)],
)
})
before(async () => {
await tbtc.mint(await stbtc.getAddress(), to1e18(6))
tx = await mezoAllocator.connect(maintainer).allocate()
})

it("should not store any tBTC in Mezo Allocator", async () => {
expect(
await tbtc.balanceOf(await mezoAllocator.getAddress()),
).to.equal(0)
})
it("should deposit and transfer tBTC to Mezo Portal", async () => {
await expect(tx).to.changeTokenBalances(
tbtc,
[await mezoPortal.getAddress()],
[to1e18(6)],
)
})

it("should increment the deposit id", async () => {
const actualDepositId = await mezoAllocator.depositId()
expect(actualDepositId).to.equal(1)
})
it("should not store any tBTC in Mezo Allocator", async () => {
expect(
await tbtc.balanceOf(await mezoAllocator.getAddress()),
).to.equal(0)
})

it("should increase tracked deposit balance amount", async () => {
const depositBalance = await mezoAllocator.depositBalance()
expect(depositBalance).to.equal(to1e18(6))
it("should increment the deposit id", async () => {
const actualDepositId = await mezoAllocator.depositId()
expect(actualDepositId).to.equal(1)
})

it("should increase tracked deposit balance amount", async () => {
const depositBalance = await mezoAllocator.depositBalance()
expect(depositBalance).to.equal(to1e18(6))
})

it("should emit DepositAllocated event", async () => {
await expect(tx)
.to.emit(mezoAllocator, "DepositAllocated")
.withArgs(0, 1, to1e18(6), to1e18(6))
})
})

it("should emit DepositAllocated event", async () => {
await expect(tx)
.to.emit(mezoAllocator, "DepositAllocated")
.withArgs(0, 1, to1e18(6), to1e18(6))
context("when a second deposit is made", () => {
let tx: ContractTransactionResponse

before(async () => {
await tbtc.mint(await stbtc.getAddress(), to1e18(5))

tx = await mezoAllocator.connect(maintainer).allocate()
})

it("should increment the deposit id", async () => {
const actualDepositId = await mezoAllocator.depositId()
expect(actualDepositId).to.equal(2)
})

it("should emit DepositAllocated event", async () => {
await expect(tx)
.to.emit(mezoAllocator, "DepositAllocated")
.withArgs(1, 2, to1e18(5), to1e18(11))
})

it("should deposit and transfer tBTC to Mezo Portal", async () => {
expect(
await tbtc.balanceOf(await mezoPortal.getAddress()),
).to.equal(to1e18(11))
})

it("should increase tracked deposit balance amount", async () => {
const depositBalance = await mezoAllocator.depositBalance()
expect(depositBalance).to.equal(to1e18(11))
})

it("should not store any tBTC in Mezo Allocator", async () => {
expect(
await tbtc.balanceOf(await mezoAllocator.getAddress()),
).to.equal(0)
})

it("should not store any tBTC in stBTC", async () => {
expect(await tbtc.balanceOf(await stbtc.getAddress())).to.equal(0)
})
})
})

context("when a second deposit is made", () => {
let tx: ContractTransactionResponse
context("when accounting for tBTC 'donation' to Mezo Allocator", () => {
let depositorDepositTx: ContractTransactionResponse
let depositorRedeemTx: ContractTransactionResponse
let depositor2DepositTx: ContractTransactionResponse
let depositor2RedeemTx: ContractTransactionResponse

beforeAfterSnapshotWrapper()

before(async () => {
await tbtc.mint(await stbtc.getAddress(), to1e18(5))
await tbtc.mint(depositor, to1e18(1))
await tbtc
.connect(depositor)
.approve(await stbtc.getAddress(), to1e18(1))
// Deposit by the first depositor
depositorDepositTx = await stbtc
.connect(depositor)
.deposit(to1e18(1), depositor)

// Mezo Portal first allocation
await mezoAllocator.connect(maintainer).allocate()

tx = await mezoAllocator.connect(maintainer).allocate()
})
// Donation / rewards
await tbtc.mint(await mezoAllocator.getAddress(), to1e18(1))

await tbtc.mint(depositor2, to1e18(1))
await tbtc
.connect(depositor2)
.approve(await stbtc.getAddress(), to1e18(1))
// Deposit by the second depositor
depositor2DepositTx = await stbtc
.connect(depositor2)
.deposit(to1e18(1), depositor2)
// Mezo Portal second allocation
await mezoAllocator.connect(maintainer).allocate()

it("should increment the deposit id", async () => {
const actualDepositId = await mezoAllocator.depositId()
expect(actualDepositId).to.equal(2)
})
// Redeeming shares by the first depositor
const stBTCdepositorBalance = await stbtc.balanceOf(depositor)
depositorRedeemTx = await stbtc
.connect(depositor)
.redeem(stBTCdepositorBalance, depositor, depositor)

it("should emit DepositAllocated event", async () => {
await expect(tx)
.to.emit(mezoAllocator, "DepositAllocated")
.withArgs(1, 2, to1e18(5), to1e18(11))
// Redeeming shares by the second depositor
const stBTCdepositor2Balance = await stbtc.balanceOf(depositor2)
depositor2RedeemTx = await stbtc
.connect(depositor2)
.redeem(stBTCdepositor2Balance, depositor2, depositor2)
})

it("should deposit and transfer tBTC to Mezo Portal", async () => {
expect(await tbtc.balanceOf(await mezoPortal.getAddress())).to.equal(
to1e18(11),
it("should mint correct amount of shares for the first depositor", async () => {
await expect(depositorDepositTx).to.changeTokenBalances(
stbtc,
[depositor.address],
[to1e18(1)],
)
})

it("should increase tracked deposit balance amount", async () => {
const depositBalance = await mezoAllocator.depositBalance()
expect(depositBalance).to.equal(to1e18(11))
it("should mint correct amount of shares for the second depositor", async () => {
// expected shares = (assets * total supply of shares) / total assets
// expected shares = (1 * 1 stBTC) / 2 tBTC = 0.5
await expect(depositor2DepositTx).to.changeTokenBalances(
stbtc,
[depositor2.address],
[500000000000000000n], // 0.5 stBTC
)
})

it("should not store any tBTC in Mezo Allocator", async () => {
expect(
await tbtc.balanceOf(await mezoAllocator.getAddress()),
).to.equal(0)
it("should redeem shares with accounting for 'donation' for the first depositor", async () => {
// expected tBTC = shares * total assets / total supply of shares
// expected tBTC = (1 * 3) / 1.5 = 2
await expect(depositorRedeemTx).to.changeTokenBalances(
tbtc,
[depositor.address],
[to1e18(2) - 1n], // adjusted for rounding
)
})

it("should not store any tBTC in stBTC", async () => {
expect(await tbtc.balanceOf(await stbtc.getAddress())).to.equal(0)
it("should redeem shares without accounting for 'donation' for the second depositor", async () => {
// expected tBTC = shares * total assets / total supply of shares
// expected tBTC = (0.5 * 3) / 1.5 = 2
await expect(depositor2RedeemTx).to.changeTokenBalances(
tbtc,
[depositor2.address],
[to1e18(1)],
)
})
})
})
Expand Down

0 comments on commit bb42bce

Please sign in to comment.