Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xERC4626 support #241

Closed
wants to merge 16 commits into from
Closed

xERC4626 support #241

wants to merge 16 commits into from

Conversation

dimpar
Copy link
Member

@dimpar dimpar commented Feb 12, 2024

xERC4626 adds support for rewards (yield) distribution gradually over the rewards cycle length period.

xERC4626 adds support for rewards (yield) distribution gradually
over the rewards cycle length period.
@dimpar dimpar self-assigned this Feb 12, 2024
@dimpar dimpar added the 🔗 Solidity Solidity contracts label Feb 12, 2024
@dimpar dimpar mentioned this pull request Feb 12, 2024
Base automatically changed from removing-fees-from-acre to main February 13, 2024 14:02
xERC4626 introduces gradual distribution of yield (rewards). In this
commit we check if the distribution was handled correctly mid cycle
after the syncRewards was called.
@dimpar dimpar requested a review from nkuba February 13, 2024 16:16
@dimpar dimpar marked this pull request as ready for review February 13, 2024 16:19
core/contracts/interfaces/IxERC4626.sol Outdated Show resolved Hide resolved
core/contracts/lib/xERC4626.sol Outdated Show resolved Hide resolved
These functions will change once the allocator contract is added, but
for now they are almost exact copy from ERC4626 with beforeWithdraw
function added to comply with xERC4626 extention.

Added tests.
Moved 2 events from this interface to implementation file
Removed this inteface because there's no direct usage of it in our
project.
core/contracts/stBTC.sol Show resolved Hide resolved
core/test/stBTC.test.ts Show resolved Hide resolved
core/contracts/lib/xERC4626.sol Show resolved Hide resolved
Simulated rewards sync after half a cycle passed.
Copy link

netlify bot commented Mar 2, 2024

‼️ Deploy request for acre-dapp-testnet rejected.

Learn more in the Netlify docs

Name Link
🔨 Latest commit 668f5fc

core/contracts/stBTC.sol Show resolved Hide resolved
core/contracts/stBTC.sol Show resolved Hide resolved
core/test/stBTC.test.ts Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For each deposit, mint, redeem, and withdraw we should add checks that storedTotalAssets values got updated, to confirm that beforeWithdraw and afterDeposit were called correctly.

Copy link
Member Author

@dimpar dimpar Mar 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

storedTotalAssets is an internal var that is checked through the public functions. If you comment out the code inside of beforeWithdraw and afterDeposit 20 out of 44 tests in stBTC will fail (as of writing this comment). These failed tests touch deposit, withdraw, mint, and redeem. I think it's a good check that storedTotalAssets is being updated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests check indirectly the storedTotalAssets being updated by checking totalAssets().

I don't want to block this PR, but when working on further updates to xERC4626 and Allocator assets tracking we should revisit this discussion.

dimpar added 4 commits March 11, 2024 21:37
xERC4626 introduced a 7 days linear distribution of rewards. Now we need
to call a syncRewards simulation test function to jump 7 days ahead so
that minted "rewards" in tests mimic the real world scenario.
Comment on lines 1432 to 1433
await tbtc.mint(await stbtc.getAddress(), currentTotalAssets)
await syncRewards(1n)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we want to simulate a deposit here we should switch to use the exact way the deposit is being made. The previous solution was simplification, minting tokens directly to stBTC contract, but it no longer work with how total assets are calculated in xERC4626.

I propose we update these two lines:

Suggested change
await tbtc.mint(await stbtc.getAddress(), currentTotalAssets)
await syncRewards(1n)
await simulateDeposit(currentTotalAssets)

And replace syncRewards helper function with simulateDeposit:

  async function simulateDeposit(amountToDeposit: bigint) {
    await tbtc.mint(await thirdParty.getAddress(), amountToDeposit)
    await tbtc
      .connect(thirdParty)
      .approve(await stbtc.getAddress(), amountToDeposit)
    await stbtc
      .connect(thirdParty)
      .deposit(amountToDeposit, await thirdParty.getAddress())
  }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 385 to 400
// check approximate value +1%
expect(await stbtc.totalAssets()).to.be.lt(
((actualDepositAmount1 +
actualDepositAmount2 +
earnedYield / 2n) *
101n) /
100n,
)
// check approximate value -1%
expect(await stbtc.totalAssets()).to.be.gt(
((actualDepositAmount1 +
actualDepositAmount2 +
earnedYield / 2n) *
99n) /
100n,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For approximate values, we could use chai's closeTo function, since it expects exact values as delta we could define a helper function:

function expectCloseTo(actual: bigint, expected: bigint, deltaPercentage = 1) {
  return expect(actual).to.be.closeTo(
    actual,
    (expected * BigInt(deltaPercentage)) / 100n,
  )
}

and use it in tests:

Suggested change
// check approximate value +1%
expect(await stbtc.totalAssets()).to.be.lt(
((actualDepositAmount1 +
actualDepositAmount2 +
earnedYield / 2n) *
101n) /
100n,
)
// check approximate value -1%
expect(await stbtc.totalAssets()).to.be.gt(
((actualDepositAmount1 +
actualDepositAmount2 +
earnedYield / 2n) *
99n) /
100n,
)
expectCloseTo(
await stbtc.totalAssets(),
actualDepositAmount1 +
actualDepositAmount2 +
earnedYield / 2n),
)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I knew it has to exist such function, but couldn't find it and that's why I implemented it myself. Changed in 1650a42

Comment on lines 1433 to 1442
async function syncRewards(intervalDivisor: bigint) {
// sync rewards
await stbtc.syncRewards()
const blockTimestamp = BigInt(await time.latest())
const rewardsCycleEnd = await stbtc.rewardsCycleEnd()
await time.setNextBlockTimestamp(
blockTimestamp + (rewardsCycleEnd - blockTimestamp) / intervalDivisor,
)
await mine(1)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this function not only calls the syncRewards but also progresses the next cycle maybe we could rename it to something like syncAndReleaseRewards?

Another idea I had is to replace the interval divisor with percentage progress, so instead of using 2n divisor we would use 50, and instead of 1n we would use 100:

Suggested change
async function syncRewards(intervalDivisor: bigint) {
// sync rewards
await stbtc.syncRewards()
const blockTimestamp = BigInt(await time.latest())
const rewardsCycleEnd = await stbtc.rewardsCycleEnd()
await time.setNextBlockTimestamp(
blockTimestamp + (rewardsCycleEnd - blockTimestamp) / intervalDivisor,
)
await mine(1)
}
async function syncAndReleaseRewards(nextCycleProgress: number) {
// sync rewards
await stbtc.syncRewards()
// progress the next cycle to release rewards
if (nextCycleProgress > 0) {
const blockTimestamp = BigInt(await time.latest())
const rewardsCycleEnd = await stbtc.rewardsCycleEnd()
await time.setNextBlockTimestamp(
blockTimestamp +
((rewardsCycleEnd - blockTimestamp) * BigInt(nextCycleProgress)) /
100n,
)
await mine(1)
}
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dimpar added 2 commits March 12, 2024 15:01
- Replaced in-house approximation function with chai's closeTo function
- Refactored syncRewards to use cycle progress instead of interval
  divisors. Renamed a helpre function to syncAndReleaseRewards
@nkuba nkuba closed this Mar 18, 2024
@nkuba
Copy link
Member

nkuba commented Mar 18, 2024

We're not introducing this feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔗 Solidity Solidity contracts
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants