-
Notifications
You must be signed in to change notification settings - Fork 7
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
Carson/decimal testing #18
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
1df9d53
chore: renamed math test file
CarsonCase 37153f6
test: Work In Progress, for team to see
CarsonCase 9ffceab
test: jamie wbtc scenario
CarsonCase 1954e86
test: jamie btc scenario changes
CarsonCase 3d9635b
test: rate math
CarsonCase 0364552
refactor: codespell
CarsonCase File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
{ | ||
"base": "0xee44150250AfF3E6aC25539765F056EDb7F85D7B", | ||
"protocolAdmin": "0x0000000000417626Ef34D62C4DC189b021603f2F", | ||
"boringVaultAndBaseDecimals": "18", | ||
"boringVault": { | ||
"boringVaultSalt": "0x1ddd634c506ad203da17ff000000000000000000000000000000000000000013", | ||
"boringVaultName": "Form LST", | ||
"boringVaultSymbol": "FLST", | ||
"address": "0x0000000000000000000000000000000000000000" | ||
}, | ||
"manager": { | ||
"managerSalt": "0x30432d4b4ec00003b4a250000000000000000000000000000000000000000013", | ||
"address": "0x0000000000000000000000000000000000000000" | ||
}, | ||
"accountant": { | ||
"accountantSalt": "0x6a184dbea6f3cc0318679f000000000000000000000000000000000000000013", | ||
"payoutAddress": "0x0000000000417626Ef34D62C4DC189b021603f2F", | ||
"allowedExchangeRateChangeUpper": "10003", | ||
"allowedExchangeRateChangeLower": "9998", | ||
"minimumUpdateDelayInSeconds": "3600", | ||
"managementFee": "0", | ||
"address": "0x0000000000000000000000000000000000000000" | ||
}, | ||
"teller": { | ||
"tellerSalt": "0x51f8968749a56d01202c91000000000000000000000000000000000000000013", | ||
"maxGasForPeer": 100000, | ||
"minGasForPeer": 0, | ||
"peerEid": 40270, | ||
"tellerContractName": "TellerWithMultiAssetSupport", | ||
"opMessenger": "0x58Cc85b8D04EA49cC6DBd3CbFFd00B4B8D6cb3ef", | ||
"assets": [], | ||
"dvnIfNoDefault": { | ||
"required": [ | ||
"0x589dEDbD617e0CBcB916A9223F4d1300c294236b" | ||
], | ||
"optional": [ | ||
"0x380275805876Ff19055EA900CDb2B46a94ecF20D", | ||
"0x8FafAE7Dd957044088b3d0F67359C327c6200d18", | ||
"0xa59BA433ac34D2927232918Ef5B2eaAfcF130BA5", | ||
"0xe552485d02EDd3067FE7FCbD4dd56BB1D3A998D2" | ||
], | ||
"blockConfirmationsRequiredIfNoDefault": 15, | ||
"optionalThreshold": 1 | ||
}, | ||
"address": "0x0000000000000000000000000000000000000000" | ||
}, | ||
"rolesAuthority": { | ||
"rolesAuthoritySalt": "0x66bbc3b3b3000b01466a3a000000000000000000000000000000000000000013", | ||
"strategist": "0x0000000000417626Ef34D62C4DC189b021603f2F", | ||
"exchangeRateBot": "0x0000000000417626Ef34D62C4DC189b021603f2F", | ||
"address": "0x0000000000000000000000000000000000000000" | ||
}, | ||
"decoder": { | ||
"decoderSalt": "0x48b53893da2e0b0248268c000000000000000000000000000000000000000013", | ||
"address": "0x0000000000000000000000000000000000000000" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,298 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity 0.8.21; | ||
|
||
import { FixedPointMathLib } from "@solmate/utils/FixedPointMathLib.sol"; | ||
import { ERC20 } from "@solmate/tokens/ERC20.sol"; | ||
import { Test, stdStorage, StdStorage, stdError, console } from "@forge-std/Test.sol"; | ||
|
||
contract RateMath is Test { | ||
using FixedPointMathLib for uint256; | ||
|
||
uint256 constant ACCEPTED_DELTA_PERCENT_OUT_OF_FAVOR = 0.000015e18; | ||
uint256 constant ACCEPTED_DELTA_PERCENT_IN_FAVOR = 0.01e18; | ||
|
||
// keep some state variables that each test can change according to the scenario it's testing | ||
uint256 ONE_SHARE; | ||
|
||
// exchange rate as reported in base decimals | ||
uint256 exchangeRateInBase; | ||
// base asset decimals, ALSO the exchange rate decimals | ||
uint256 baseDecimals; | ||
// the quote asset decimals | ||
uint256 quoteDecimals; | ||
// decimals returned by rate provider in base per quote | ||
uint256 quoteRateDecimals; | ||
// quote rate returned by rate provider | ||
uint256 quoteRate; | ||
|
||
function boundValues( | ||
uint256 depositAmount, | ||
uint256 startQuoteRate, | ||
uint256 startExchangeRate | ||
) | ||
internal | ||
returns (uint256 _depositAmount, uint256 _quoteRate, uint256 _exchangeRate) | ||
{ | ||
// Bound Deposit 1 - 100,000,000 QuoteDecimals | ||
_depositAmount = bound(depositAmount, 1 * e(quoteDecimals), 100_000_000 * e(quoteDecimals)); | ||
// Bound quote rate to 0.01 - 10 QuoteRateDecimals | ||
_quoteRate = bound(startQuoteRate, 1 * e(quoteRateDecimals - 2), 10 * e(quoteRateDecimals)); | ||
// bound exchange rate to 0.8 - 2 baseDecimals | ||
_exchangeRate = bound(startExchangeRate, 8 * e(baseDecimals - 1), 2 * e(baseDecimals)); | ||
} | ||
|
||
function testAtomicDepositAndWithdraw_18Decimals( | ||
uint256 depositAmount, | ||
uint256 startQuoteRate, | ||
uint256 startExchangeRate | ||
) | ||
external | ||
{ | ||
// set decimals | ||
baseDecimals = 18; | ||
quoteDecimals = 18; | ||
quoteRateDecimals = 18; | ||
ONE_SHARE = 10 ** baseDecimals; | ||
|
||
// bound values with helper function | ||
(depositAmount, quoteRate, exchangeRateInBase) = boundValues(depositAmount, startQuoteRate, startExchangeRate); | ||
|
||
// get shares out if deposit done | ||
uint256 shares = depositAssetForShares(depositAmount); | ||
|
||
// get assets back if all shares are withdrawn immediately | ||
uint256 assetsBack = withdrawSharesForAssets(shares); | ||
assertTrue(assetsBack <= depositAmount, "Users should never get back more assets than they deposited"); | ||
assertApproxEqAbs( | ||
assetsBack, | ||
depositAmount, | ||
depositAmount.mulDivDown(ACCEPTED_DELTA_PERCENT_IN_FAVOR, 1e18), | ||
"assetsBack != depositAmount when atomic | In Favor" | ||
); | ||
} | ||
|
||
function testDepositAndWithdrawWithExchangeRateChange_18_6_Decimals( | ||
uint256 depositAmount, | ||
uint256 startQuoteRate, | ||
uint256 startExchangeRate, | ||
uint256 rateChange | ||
) | ||
external | ||
{ | ||
// set decimals | ||
baseDecimals = 18; | ||
quoteDecimals = 6; | ||
quoteRateDecimals = 6; | ||
ONE_SHARE = 10 ** baseDecimals; | ||
|
||
// bound values | ||
(depositAmount, startQuoteRate, startExchangeRate) = | ||
boundValues(depositAmount, startQuoteRate, startExchangeRate); | ||
exchangeRateInBase = startExchangeRate; | ||
quoteRate = startQuoteRate; | ||
rateChange = bound(rateChange, 9980, 10_020); | ||
|
||
// get shares out if deposit done | ||
uint256 shares = depositAssetForShares(depositAmount); | ||
|
||
// update the rate according to rate change | ||
exchangeRateInBase = exchangeRateInBase.mulDivDown(rateChange, 10_000); | ||
|
||
uint256 assetsBack = withdrawSharesForAssets(shares); | ||
// get expected amount out | ||
uint256 expected = (depositAmount * exchangeRateInBase * startQuoteRate) / (quoteRate * startExchangeRate); | ||
|
||
if (assetsBack > expected) { | ||
assertApproxEqAbs( | ||
assetsBack, | ||
expected, | ||
expected.mulDivDown(ACCEPTED_DELTA_PERCENT_OUT_OF_FAVOR, 1e18), | ||
"assetsBack != depositAmount with rate change | Out Of Favor" | ||
); | ||
} | ||
assertApproxEqAbs( | ||
assetsBack, | ||
expected, | ||
expected.mulDivDown(ACCEPTED_DELTA_PERCENT_IN_FAVOR, 1e18), | ||
"assetsBack != depositAmount with rate change | In Favor" | ||
); | ||
} | ||
|
||
function testDepositAndWithdrawWithQuoteRateChange_18_6_Decimals( | ||
uint256 depositAmount, | ||
uint256 startQuoteRate, | ||
uint256 startExchangeRate, | ||
uint256 rateChange | ||
) | ||
external | ||
{ | ||
// set decimals | ||
baseDecimals = 18; | ||
quoteDecimals = 6; | ||
quoteRateDecimals = 6; | ||
ONE_SHARE = 10 ** baseDecimals; | ||
|
||
// bound values | ||
(depositAmount, startQuoteRate, startExchangeRate) = | ||
boundValues(depositAmount, startQuoteRate, startExchangeRate); | ||
exchangeRateInBase = startExchangeRate; | ||
quoteRate = startQuoteRate; | ||
rateChange = bound(rateChange, 9980, 10_020); | ||
|
||
// get shares out if deposit done | ||
uint256 shares = depositAssetForShares(depositAmount); | ||
|
||
// update the rate according to rate change | ||
quoteRate = quoteRate.mulDivDown(rateChange, 10_000); | ||
|
||
uint256 assetsBack = withdrawSharesForAssets(shares); | ||
|
||
// get expected amount out | ||
uint256 expected = (depositAmount * exchangeRateInBase * startQuoteRate) / (quoteRate * startExchangeRate); | ||
|
||
if (assetsBack > expected) { | ||
assertApproxEqAbs( | ||
assetsBack, | ||
expected, | ||
expected.mulDivDown(ACCEPTED_DELTA_PERCENT_OUT_OF_FAVOR, 1e18), | ||
"assetsBack != depositAmount with rate change | Out Of Favor" | ||
); | ||
} | ||
assertApproxEqAbs( | ||
assetsBack, | ||
expected, | ||
expected.mulDivDown(ACCEPTED_DELTA_PERCENT_IN_FAVOR, 1e18), | ||
"assetsBack != depositAmount with rate change | In Favor" | ||
); | ||
} | ||
|
||
function testDepositAndWithdrawWithAllFuzzed_18_decimals( | ||
uint256 depositAmount, | ||
uint256 startQuoteRate, | ||
uint256 startExchangeRate, | ||
uint256 exchangeRateChange, | ||
uint256 quoteRateChange | ||
) | ||
external | ||
{ | ||
// set decimals | ||
baseDecimals = 18; | ||
quoteDecimals = 18; | ||
quoteRateDecimals = quoteDecimals; | ||
ONE_SHARE = 10 ** baseDecimals; | ||
|
||
// bound values | ||
(depositAmount, startQuoteRate, startExchangeRate) = | ||
boundValues(depositAmount, startQuoteRate, startExchangeRate); | ||
exchangeRateInBase = startExchangeRate; | ||
quoteRate = startQuoteRate; | ||
exchangeRateChange = bound(exchangeRateChange, 5980, 20_020); | ||
quoteRateChange = bound(quoteRateChange, 5980, 20_020); | ||
|
||
// get shares out if deposit done | ||
uint256 shares = depositAssetForShares(depositAmount); | ||
|
||
// update the rate according to rate change | ||
exchangeRateInBase = exchangeRateInBase.mulDivDown(exchangeRateChange, 10_000); | ||
quoteRate = quoteRate.mulDivDown(quoteRateChange, 10_000); | ||
|
||
uint256 expected = (depositAmount * exchangeRateInBase * startQuoteRate) / (quoteRate * startExchangeRate); | ||
|
||
uint256 assetsBack = withdrawSharesForAssets(shares); | ||
|
||
if (assetsBack > expected) { | ||
assertApproxEqAbs( | ||
assetsBack, | ||
expected, | ||
expected.mulDivDown(ACCEPTED_DELTA_PERCENT_OUT_OF_FAVOR, 1e18), | ||
"assetsBack != depositAmount with rate change | Out Of Favor" | ||
); | ||
} | ||
assertApproxEqAbs( | ||
assetsBack, | ||
expected, | ||
expected.mulDivDown(ACCEPTED_DELTA_PERCENT_IN_FAVOR, 1e18), | ||
"assetsBack != depositAmount with rate change | In Favor" | ||
); | ||
} | ||
|
||
function testDepositAndWithdrawWithAllFuzzed_18_6_decimals( | ||
uint256 depositAmount, | ||
uint256 startQuoteRate, | ||
uint256 startExchangeRate, | ||
uint256 exchangeRateChange, | ||
uint256 quoteRateChange | ||
) | ||
external | ||
{ | ||
// set decimals | ||
baseDecimals = 18; | ||
quoteDecimals = 6; | ||
quoteRateDecimals = quoteDecimals; | ||
ONE_SHARE = 10 ** baseDecimals; | ||
|
||
// bound values | ||
(depositAmount, startQuoteRate, startExchangeRate) = | ||
boundValues(depositAmount, startQuoteRate, startExchangeRate); | ||
exchangeRateInBase = startExchangeRate; | ||
quoteRate = startQuoteRate; | ||
exchangeRateChange = bound(exchangeRateChange, 5980, 20_020); | ||
quoteRateChange = bound(quoteRateChange, 5980, 20_020); | ||
|
||
// get shares out if deposit done | ||
uint256 shares = depositAssetForShares(depositAmount); | ||
|
||
// update the rate according to rate change | ||
exchangeRateInBase = exchangeRateInBase.mulDivDown(exchangeRateChange, 10_000); | ||
quoteRate = quoteRate.mulDivDown(quoteRateChange, 10_000); | ||
|
||
uint256 expected = (depositAmount * exchangeRateInBase * startQuoteRate) / (quoteRate * startExchangeRate); | ||
|
||
uint256 assetsBack = withdrawSharesForAssets(shares); | ||
|
||
if (assetsBack > expected) { | ||
assertApproxEqAbs( | ||
assetsBack, | ||
expected, | ||
expected.mulDivDown(ACCEPTED_DELTA_PERCENT_OUT_OF_FAVOR, 1e18), | ||
"assetsBack != depositAmount with rate change | Out Of Favor" | ||
); | ||
} | ||
assertApproxEqAbs( | ||
assetsBack, | ||
expected, | ||
expected.mulDivDown(ACCEPTED_DELTA_PERCENT_IN_FAVOR, 1e18), | ||
"assetsBack != depositAmount with rate change | In Favor" | ||
); | ||
} | ||
|
||
function withdrawSharesForAssets(uint256 shareAmount) public view returns (uint256 assetsOut) { | ||
assetsOut = shareAmount.mulDivDown(getRateInQuote(), ONE_SHARE); | ||
} | ||
|
||
function depositAssetForShares(uint256 depositAmount) public view returns (uint256 shares) { | ||
if (depositAmount == 0) revert("depositAssetForShares amount = 0"); | ||
shares = depositAmount.mulDivDown(ONE_SHARE, getRateInQuote()); | ||
} | ||
|
||
function getRateInQuote() public view returns (uint256 rateInQuote) { | ||
uint256 exchangeRateInQuoteDecimals = changeDecimals(exchangeRateInBase, baseDecimals, quoteDecimals); | ||
uint256 oneQuote = 10 ** quoteDecimals; | ||
rateInQuote = oneQuote.mulDivDown((exchangeRateInQuoteDecimals), quoteRate); | ||
} | ||
|
||
function changeDecimals(uint256 amount, uint256 fromDecimals, uint256 toDecimals) internal pure returns (uint256) { | ||
if (fromDecimals == toDecimals) { | ||
return amount; | ||
} else if (fromDecimals < toDecimals) { | ||
return amount * 10 ** (toDecimals - fromDecimals); | ||
} else { | ||
return amount / 10 ** (fromDecimals - toDecimals); | ||
} | ||
} | ||
|
||
/// @dev Helper function to perform 10**x | ||
function e(uint256 decimals) internal pure returns (uint256) { | ||
return (10 ** decimals); | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loosen bounds for specific tests @CarsonCase