diff --git a/.env.example b/.env.example index 46252a8..d999357 100644 --- a/.env.example +++ b/.env.example @@ -3,7 +3,7 @@ OPTIMIZER_ENABLED = true OPTIMIZER_RUNS = 1000 GAS_REPORTER_ENABLED = false CONTRACT_SIZER_ENABLED = false -ALLOW_UNLIMITED_CONTRACT_SIZE = false +ALLOW_UNLIMITED_CONTRACT_SIZE = true HARDHAT_MNEMONIC = test test test test test test test test test test test junk GANACHE_RPC = http://127.0.0.1:7545 GANACHE_MNEMONIC = test test test test test test test test test test test junk diff --git a/contracts/YieldStreamer.sol b/contracts/YieldStreamer.sol index 5e12422..174534e 100644 --- a/contracts/YieldStreamer.sol +++ b/contracts/YieldStreamer.sol @@ -119,8 +119,8 @@ contract YieldStreamer is /** * @inheritdoc IYieldStreamerPrimary_Functions */ - function getClaimPreview(address account) external view returns (ClaimPreview memory) { - return _getClaimPreview(account, _blockTimestamp()); + function getClaimPreview(address account, bool round) external view returns (ClaimPreview memory) { + return _getClaimPreview(account, _blockTimestamp(), round); } /** diff --git a/contracts/YieldStreamerPrimary.sol b/contracts/YieldStreamerPrimary.sol index 08718c8..a925480 100644 --- a/contracts/YieldStreamerPrimary.sol +++ b/contracts/YieldStreamerPrimary.sol @@ -253,11 +253,11 @@ abstract contract YieldStreamerPrimary is * @param account The account to get the claim preview for. * @return A `ClaimPreview` struct containing details of the claimable yield. */ - function _getClaimPreview(address account, uint256 currentTimestamp) internal view returns (ClaimPreview memory) { + function _getClaimPreview(address account, uint256 currentTimestamp, bool round) internal view returns (ClaimPreview memory) { YieldStreamerStorageLayout storage $ = _yieldStreamerStorage(); YieldState storage state = $.yieldStates[account]; YieldRate[] storage rates = $.yieldRates[$.groups[account].id]; - return _map(_getAccruePreview(state, rates, currentTimestamp)); + return _map(_getAccruePreview(state, rates, currentTimestamp), round); } /** @@ -1031,11 +1031,12 @@ abstract contract YieldStreamerPrimary is * @dev Maps an `AccruePreview` struct to a `ClaimPreview` struct. * * @param accruePreview The `AccruePreview` struct to map from. + * @param round Whether to round the yield and fee amounts. * @return claimPreview The resulting `ClaimPreview` struct. */ - function _map(AccruePreview memory accruePreview) internal pure returns (ClaimPreview memory claimPreview) { + function _map(AccruePreview memory accruePreview, bool round) internal pure returns (ClaimPreview memory claimPreview) { uint256 totalYield = accruePreview.accruedYieldAfter + accruePreview.streamYieldAfter; - claimPreview.yield = _roundDown(totalYield); + claimPreview.yield = round ? _roundDown(totalYield) : totalYield; claimPreview.fee = 0; // Fees are not supported yet. claimPreview.timestamp = accruePreview.toTimestamp; claimPreview.balance = accruePreview.balance; diff --git a/contracts/base/Versionable.sol b/contracts/base/Versionable.sol index 301d463..3d98f03 100644 --- a/contracts/base/Versionable.sol +++ b/contracts/base/Versionable.sol @@ -16,6 +16,6 @@ abstract contract Versionable is IVersionable { * @inheritdoc IVersionable */ function $__VERSION() external pure returns (Version memory) { - return Version(2, 0, 0); + return Version(2, 1, 0); } } diff --git a/contracts/interfaces/IYieldStreamerPrimary.sol b/contracts/interfaces/IYieldStreamerPrimary.sol index 711b4bb..44539fd 100644 --- a/contracts/interfaces/IYieldStreamerPrimary.sol +++ b/contracts/interfaces/IYieldStreamerPrimary.sol @@ -110,9 +110,13 @@ interface IYieldStreamerPrimary_Functions { * Estimates the yield amount that can be claimed without modifying the contract state. * * @param account The address of the account to query. + * @param round Whether to round the yield and fee amounts. * @return A `ClaimPreview` struct containing details of the claimable yield. */ - function getClaimPreview(address account) external view returns (IYieldStreamerTypes.ClaimPreview memory); + function getClaimPreview( + address account, + bool round + ) external view returns (IYieldStreamerTypes.ClaimPreview memory); /** * @dev Provides a preview of the yield accrual for a given account over time. diff --git a/contracts/interfaces/IYieldStreamerTypes.sol b/contracts/interfaces/IYieldStreamerTypes.sol index e627988..91cbcfb 100644 --- a/contracts/interfaces/IYieldStreamerTypes.sol +++ b/contracts/interfaces/IYieldStreamerTypes.sol @@ -84,7 +84,7 @@ interface IYieldStreamerTypes { * Used to estimate the yield that can be claimed without modifying the contract state. * * Fields: - * - `yield`: The total claimable yield amount (rounded down) available for the account. + * - `yield`: The total claimable yield amount available for the account. * - `fee`: The fee amount that would be deducted during the claim. * - `timestamp`: The timestamp at which the preview was calculated. * - `balance`: The account's token balance used in the calculation. diff --git a/contracts/testable/YieldStreamerTestable.sol b/contracts/testable/YieldStreamerTestable.sol index 0b1b71e..6d0af5d 100644 --- a/contracts/testable/YieldStreamerTestable.sol +++ b/contracts/testable/YieldStreamerTestable.sol @@ -88,7 +88,7 @@ contract YieldStreamerTestable is YieldStreamer { return _roundUp(amount); } - function map(AccruePreview memory accrue) external pure returns (ClaimPreview memory) { - return _map(accrue); + function map(AccruePreview memory accrue, bool round) external pure returns (ClaimPreview memory) { + return _map(accrue, round); } } diff --git a/hardhat.config.ts b/hardhat.config.ts index 25c9c8c..434fc21 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -22,10 +22,7 @@ const config: HardhatUserConfig = { accounts: { mnemonic: process.env.HARDHAT_MNEMONIC }, - allowUnlimitedContractSize: - process.env.ALLOW_UNLIMITED_CONTRACT_SIZE !== undefined - ? process.env.ALLOW_UNLIMITED_CONTRACT_SIZE === "true" - : true + allowUnlimitedContractSize: process.env.ALLOW_UNLIMITED_CONTRACT_SIZE === "true" }, ganache: { url: process.env.GANACHE_RPC, diff --git a/test/YieldStreamer.schedule.test.ts b/test/YieldStreamer.schedule.test.ts index bfa71e0..e0c9c03 100644 --- a/test/YieldStreamer.schedule.test.ts +++ b/test/YieldStreamer.schedule.test.ts @@ -173,7 +173,7 @@ describe("YieldStreamer - Deposit/Withdraw Simulation Tests", function () { let adjustedBlockTime: number; const EXPECTED_VERSION: Version = { major: 2, - minor: 0, + minor: 1, patch: 0 }; diff --git a/test/YieldStreamerHarness.test.ts b/test/YieldStreamerHarness.test.ts index be598bb..44e8a17 100644 --- a/test/YieldStreamerHarness.test.ts +++ b/test/YieldStreamerHarness.test.ts @@ -77,7 +77,7 @@ describe("YieldStreamerHarness", function () { const harnessAdminRole: string = ethers.id("HARNESS_ADMIN_ROLE"); const EXPECTED_VERSION: Version = { major: 2, - minor: 0, + minor: 1, patch: 0 }; diff --git a/test/YieldStreamerTestable.test.ts b/test/YieldStreamerTestable.test.ts index 38cf77d..7116797 100644 --- a/test/YieldStreamerTestable.test.ts +++ b/test/YieldStreamerTestable.test.ts @@ -2290,60 +2290,88 @@ describe("YieldStreamerTestable", async () => { }); describe("Function 'map()'", async () => { - it("Should map as expected", async () => { + // Create an `AccruePreview` struct with sample data + const accruePreview: AccruePreview = { + fromTimestamp: 10000000n, + toTimestamp: 20000000n, + balance: 30000000n, + streamYieldBefore: 199996n, + accruedYieldBefore: 299996n, + streamYieldAfter: 499996n, + accruedYieldAfter: 399996n, + rates: [ + { + tiers: [ + { rate: 101n, cap: 102n }, + { rate: 201n, cap: 202n } + ], + effectiveDay: 1n + }, + { + tiers: [ + { rate: 301n, cap: 302n }, + { rate: 401n, cap: 402n } + ], + effectiveDay: 9n + } + ], + results: [ + { + partialFirstDayYield: 111n, + fullDaysYield: 211n, + partialLastDayYield: 311n, + partialFirstDayYieldTiered: [101n, 10n], + fullDaysYieldTiered: [201n, 10n], + partialLastDayYieldTiered: [301n, 10n] + }, + { + partialFirstDayYield: 411n, + fullDaysYield: 511n, + partialLastDayYield: 611n, + partialFirstDayYieldTiered: [401n, 10n], + fullDaysYieldTiered: [501n, 10n], + partialLastDayYieldTiered: [601n, 10n] + } + ] + }; + + it("Should map as expected (rounded)", async () => { const { yieldStreamerTestable } = await setUpFixture(deployContracts); - // Create an `AccruePreview` struct with sample data - const accruePreview: AccruePreview = { - fromTimestamp: 10000000n, - toTimestamp: 20000000n, - balance: 30000000n, - streamYieldBefore: 199996n, - accruedYieldBefore: 299996n, - streamYieldAfter: 499996n, - accruedYieldAfter: 399996n, - rates: [ - { - tiers: [ - { rate: 101n, cap: 102n }, - { rate: 201n, cap: 202n } - ], - effectiveDay: 1n - }, - { - tiers: [ - { rate: 301n, cap: 302n }, - { rate: 401n, cap: 402n } - ], - effectiveDay: 9n - } - ], - results: [ - { - partialFirstDayYield: 111n, - fullDaysYield: 211n, - partialLastDayYield: 311n, - partialFirstDayYieldTiered: [101n, 10n], - fullDaysYieldTiered: [201n, 10n], - partialLastDayYieldTiered: [301n, 10n] - }, - { - partialFirstDayYield: 411n, - fullDaysYield: 511n, - partialLastDayYield: 611n, - partialFirstDayYieldTiered: [401n, 10n], - fullDaysYieldTiered: [501n, 10n], - partialLastDayYieldTiered: [601n, 10n] - } - ] + // Call the `map` function + const claimPreviewRaw: ClaimPreview = await yieldStreamerTestable.map(accruePreview, true); + + // Create the `ClaimPreview` struct with expected values + const expectedClaimPreview: ClaimPreview = { + yield: roundDown(accruePreview.accruedYieldAfter + accruePreview.streamYieldAfter), + balance: accruePreview.balance, + fee: 0n, + timestamp: accruePreview.toTimestamp, + rates: accruePreview.rates[accruePreview.rates.length - 1].tiers.map(tier => tier.rate), + caps: accruePreview.rates[accruePreview.rates.length - 1].tiers.map(tier => tier.cap) }; + // Assertion + expect(accruePreview.accruedYieldAfter + accruePreview.streamYieldAfter).not.to.equal( + roundDown(accruePreview.accruedYieldAfter + accruePreview.streamYieldAfter) + ); + expect(expectedClaimPreview.yield).to.equal(claimPreviewRaw.yield); + expect(expectedClaimPreview.fee).to.equal(claimPreviewRaw.fee); + expect(expectedClaimPreview.timestamp).to.equal(claimPreviewRaw.timestamp); + expect(expectedClaimPreview.balance).to.equal(claimPreviewRaw.balance); + expect(expectedClaimPreview.rates).to.deep.equal(claimPreviewRaw.rates); + expect(expectedClaimPreview.caps).to.deep.equal(claimPreviewRaw.caps); + }); + + it("Should map as expected (not rounded)", async () => { + const { yieldStreamerTestable } = await setUpFixture(deployContracts); + // Call the `map` function - const claimPreviewRaw: ClaimPreview = await yieldStreamerTestable.map(accruePreview); + const claimPreviewRaw: ClaimPreview = await yieldStreamerTestable.map(accruePreview, false); // Create the `ClaimPreview` struct with expected values const expectedClaimPreview: ClaimPreview = { - yield: roundDown(accruePreview.accruedYieldAfter + accruePreview.streamYieldAfter), + yield: accruePreview.accruedYieldAfter + accruePreview.streamYieldAfter, balance: accruePreview.balance, fee: 0n, timestamp: accruePreview.toTimestamp, @@ -2352,6 +2380,9 @@ describe("YieldStreamerTestable", async () => { }; // Assertion + expect(accruePreview.accruedYieldAfter + accruePreview.streamYieldAfter).not.to.equal( + roundDown(accruePreview.accruedYieldAfter + accruePreview.streamYieldAfter) + ); expect(expectedClaimPreview.yield).to.equal(claimPreviewRaw.yield); expect(expectedClaimPreview.fee).to.equal(claimPreviewRaw.fee); expect(expectedClaimPreview.timestamp).to.equal(claimPreviewRaw.timestamp);