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

Carson/decimal testing #18

Merged
merged 6 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions deployment-config/form-lst-testnet-l1-08-30-24.json
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"
}
}
2 changes: 1 addition & 1 deletion lzConfigCheck.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ async function main() {

const chain2 = findings2.findings[0].chain;
for (const finding of findings2.findings) {
assert(providers1.includes(finding.provider), "Provider: "+finding.provider+" does not havea matching provider in the first config");
assert(providers1.includes(finding.provider), "Provider: "+finding.provider+" does not have a matching provider in the first config");
assert(finding.chain == chain2, "Networks do not match for: "+finding);
}

Expand Down
2 changes: 1 addition & 1 deletion test/LiveDeploy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { AccountantWithRateProviders } from "src/base/Roles/AccountantWithRatePr
import { RolesAuthority } from "@solmate/auth/authorities/RolesAuthority.sol";
import { DeployRateProviders } from "script/deploy/01_DeployRateProviders.s.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { StdJson } from "@forge-std/StdJson.sol";
import { stdJson as StdJson } from "@forge-std/StdJson.sol";

import { CrossChainOPTellerWithMultiAssetSupportTest } from
"test/CrossChain/CrossChainOPTellerWithMultiAssetSupport.t.sol";
Expand Down
298 changes: 298 additions & 0 deletions test/RateMath.t.sol
Copy link
Collaborator Author

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

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);
}
}
Loading
Loading