Skip to content

Commit

Permalink
fix: Adds decimal scaling to _readTwap
Browse files Browse the repository at this point in the history
  • Loading branch information
jar-o committed Aug 16, 2024
1 parent a193343 commit 54087c8
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 10 deletions.
Binary file added audits/[email protected]
Binary file not shown.
Binary file added audits/[email protected]
Binary file not shown.
2 changes: 2 additions & 0 deletions script/Aggor.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ contract AggorScript is Script {
address uniswapBaseToken,
address uniswapQuoteToken,
uint8 uniswapBaseTokenDecimals,
uint8 uniswapQuoteTokenDecimals,
uint32 uniswapLookback,
uint128 agreementDistance,
uint32 ageThreshold
Expand All @@ -40,6 +41,7 @@ contract AggorScript is Script {
uniswapBaseToken,
uniswapQuoteToken,
uniswapBaseTokenDecimals,
uniswapQuoteTokenDecimals,
uniswapLookback,
agreementDistance,
ageThreshold
Expand Down
24 changes: 22 additions & 2 deletions src/Aggor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {LibMedian} from "./libs/LibMedian.sol";

/**
* @title Aggor
* @custom:version v1.0.0
* @custom:version v1.0.1
*
* @notice Oracle aggregator distributing trust among different oracle providers
*
Expand Down Expand Up @@ -74,6 +74,8 @@ contract Aggor is IAggor, IToll, Auth {
/// @inheritdoc IAggor
uint8 public immutable uniswapBaseTokenDecimals;
/// @inheritdoc IAggor
uint8 public immutable uniswapQuoteTokenDecimals;
/// @inheritdoc IAggor
uint32 public immutable uniswapLookback;

// -- Mutable Configurations --
Expand Down Expand Up @@ -103,6 +105,7 @@ contract Aggor is IAggor, IToll, Auth {
address uniswapBaseToken_,
address uniswapQuoteToken_,
uint8 uniswapBaseTokenDecimals_,
uint8 uniswapQuoteTokenDecimals_,
uint32 uniswapLookback_,
uint128 agreementDistance_,
uint32 ageThreshold_
Expand All @@ -113,6 +116,7 @@ contract Aggor is IAggor, IToll, Auth {
uniswapBaseToken_,
uniswapQuoteToken_,
uniswapBaseTokenDecimals_,
uniswapQuoteTokenDecimals_,
uniswapLookback_
);

Expand All @@ -124,6 +128,7 @@ contract Aggor is IAggor, IToll, Auth {
uniswapBaseToken = uniswapBaseToken_;
uniswapQuoteToken = uniswapQuoteToken_;
uniswapBaseTokenDecimals = uniswapBaseTokenDecimals_;
uniswapQuoteTokenDecimals = uniswapQuoteTokenDecimals_;
uniswapLookback = uniswapLookback_;

// Emit events indicating address(0) and _bud are tolled.
Expand All @@ -142,6 +147,7 @@ contract Aggor is IAggor, IToll, Auth {
address uniswapBaseToken_,
address uniswapQuoteToken_,
uint8 uniswapBaseTokenDecimals_,
uint8 uniswapQuoteTokenDecimals_,
uint32 uniswapLookback_
) internal view {
require(uniswapPool_ != address(0), "Uniswap pool must not be zero");
Expand All @@ -163,7 +169,7 @@ contract Aggor is IAggor, IToll, Auth {
"Uniswap quote token mismatch"
);

// Verify base token's decimals.
// Verify token decimals.
require(
uniswapBaseTokenDecimals_ == IERC20(uniswapBaseToken_).decimals(),
"Uniswap base token decimals mismatch"
Expand All @@ -172,6 +178,10 @@ contract Aggor is IAggor, IToll, Auth {
uniswapBaseTokenDecimals_ <= _MAX_UNISWAP_BASE_DECIMALS,
"Uniswap base token decimals too high"
);
require(
uniswapQuoteTokenDecimals_ == IERC20(uniswapQuoteToken_).decimals(),
"Uniswap quote token decimals mismatch"
);

// Verify TWAP is initialized.
// Specifically, verify that the TWAP's oldest observation is older
Expand Down Expand Up @@ -338,6 +348,14 @@ contract Aggor is IAggor, IToll, Auth {
uniswapLookback
);

if (uniswapQuoteTokenDecimals <= decimals) {
// Scale up
twap *= 10 ** (decimals - uniswapQuoteTokenDecimals);
} else {
// Scale down
twap /= 10 ** (uniswapQuoteTokenDecimals - decimals);
}

if (twap <= type(uint128).max) {
return (true, uint128(twap));
} else {
Expand Down Expand Up @@ -488,6 +506,7 @@ contract Aggor_BASE_QUOTE_COUNTER is Aggor {
address uniswapBaseToken_,
address uniswapQuoteToken_,
uint8 uniswapBaseDec_,
uint8 uniswapQuoteDec_,
uint32 uniswapLookback_,
uint128 agreementDistance_,
uint32 ageThreshold_
Expand All @@ -501,6 +520,7 @@ contract Aggor_BASE_QUOTE_COUNTER is Aggor {
uniswapBaseToken_,
uniswapQuoteToken_,
uniswapBaseDec_,
uniswapQuoteDec_,
uniswapLookback_,
agreementDistance_,
ageThreshold_
Expand Down
7 changes: 7 additions & 0 deletions src/IAggor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ interface IAggor {
view
returns (uint8 baseTokenDecimals);

/// @notice Returns the Uniswap pool's quote token's decimals.
/// @return quoteTokenDecimals The Uniswap pool's quote token's decimals.
function uniswapQuoteTokenDecimals()
external
view
returns (uint8 quoteTokenDecimals);

/// @notice Returns the time in seconds to use as lookback for Uniswap Twap
/// oracle.
/// @return lookback The time in seconds to use as lookback.
Expand Down
32 changes: 30 additions & 2 deletions test/Aggor.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ contract AggorTest is Test {
// Twap Provider:
address uniswapPool = address(new UniswapPoolMock());
address uniswapBaseToken = address(new ERC20Mock("base", "base", 18));
address uniswapQuoteToken = address(new ERC20Mock("quote", "quote", 18));
address uniswapQuoteToken = address(new ERC20Mock("quote", "quote", 6));
uint32 uniswapLookback = 1 days;

// For more info, see mocks/UniswapPoolMock::observe().
uint valTwap = 999_902;
uint valTwap = 99_990_200; // Scaled for 10^Aggor.decimals()

// Configurations:
uint128 agreementDistance = 9e17; // = 0.9e18 = 10%
Expand All @@ -58,6 +58,7 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapQuoteToken,
IERC20(uniswapBaseToken).decimals(),
IERC20(uniswapQuoteToken).decimals(),
uniswapLookback,
agreementDistance,
ageThreshold
Expand All @@ -74,6 +75,7 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapQuoteToken,
IERC20(uniswapBaseToken).decimals(),
IERC20(uniswapQuoteToken).decimals(),
uniswapLookback,
agreementDistance,
ageThreshold
Expand All @@ -85,6 +87,7 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapQuoteToken,
IERC20(uniswapBaseToken).decimals(),
IERC20(uniswapQuoteToken).decimals(),
uniswapLookback
);
}
Expand All @@ -93,6 +96,7 @@ contract AggorTest is Test {

function test_Deployment_FailsIf_UniswapPoolZeroAddress() public {
uint8 decimals = IERC20(uniswapBaseToken).decimals();
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();

vm.expectRevert("Uniswap pool must not be zero");
new Aggor(
Expand All @@ -104,6 +108,7 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapQuoteToken,
decimals,
quoteDecimals,
uniswapLookback,
agreementDistance,
ageThreshold
Expand All @@ -115,12 +120,14 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapQuoteToken,
decimals,
quoteDecimals,
uniswapLookback
);
}

function test_Deployment_FailsIf_BaseTokenEqualsQuoteToken() public {
uint8 decimals = IERC20(uniswapBaseToken).decimals();
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();

vm.expectRevert("Uniswap tokens must not be equal");
new Aggor(
Expand All @@ -132,6 +139,7 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapBaseToken, // <- !
decimals,
quoteDecimals,
uniswapLookback,
agreementDistance,
ageThreshold
Expand All @@ -143,13 +151,15 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapBaseToken, // <- !
decimals,
quoteDecimals,
uniswapLookback
);
}

function test_Deployment_FailsIf_BaseTokenNotPoolToken() public {
address notPoolToken = address(new ERC20Mock("", "", 18));
uint8 decimals = IERC20(notPoolToken).decimals();
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();

vm.expectRevert("Uniswap base token mismatch");
new Aggor(
Expand All @@ -161,6 +171,7 @@ contract AggorTest is Test {
notPoolToken, // <- !
uniswapQuoteToken,
decimals,
quoteDecimals,
uniswapLookback,
agreementDistance,
ageThreshold
Expand All @@ -172,13 +183,15 @@ contract AggorTest is Test {
notPoolToken, // <- !
uniswapQuoteToken,
decimals,
quoteDecimals,
uniswapLookback
);
}

function test_Deployment_FailsIf_QuoteTokenNotPoolToken() public {
address notPoolToken = address(new ERC20Mock("", "", 18));
uint8 decimals = IERC20(uniswapBaseToken).decimals();
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();

vm.expectRevert("Uniswap quote token mismatch");
new Aggor(
Expand All @@ -190,6 +203,7 @@ contract AggorTest is Test {
uniswapBaseToken,
notPoolToken, // <- !
decimals,
quoteDecimals,
uniswapLookback,
agreementDistance,
ageThreshold
Expand All @@ -201,12 +215,14 @@ contract AggorTest is Test {
uniswapBaseToken,
notPoolToken, // <- !
decimals,
quoteDecimals,
uniswapLookback
);
}

function test_Deployment_FailsIf_BaseTokenDecimalsWrong() public {
uint8 decimals = IERC20(uniswapBaseToken).decimals();
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();

vm.expectRevert("Uniswap base token decimals mismatch");
new Aggor(
Expand All @@ -218,6 +234,7 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapQuoteToken,
decimals + 1, // <- !
quoteDecimals,
uniswapLookback,
agreementDistance,
ageThreshold
Expand All @@ -229,6 +246,7 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapQuoteToken,
decimals + 1, // <- !
quoteDecimals,
uniswapLookback
);
}
Expand All @@ -237,6 +255,7 @@ contract AggorTest is Test {
) public {
uniswapBaseToken = address(new ERC20Mock("base", "base", 100)); // <- !
uint8 decimals = IERC20(uniswapBaseToken).decimals();
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();

UniswapPoolMock(uniswapPool).setToken0(uniswapBaseToken);

Expand All @@ -250,6 +269,7 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapQuoteToken,
decimals,
quoteDecimals,
uniswapLookback,
agreementDistance,
ageThreshold
Expand All @@ -261,13 +281,15 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapQuoteToken,
decimals,
quoteDecimals,
uniswapLookback
);
}

function test_Deployment_FailsIf_UniswapLookbackBiggerThanOldestObservation(
) public {
uint8 decimals = IERC20(uniswapBaseToken).decimals();
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();

vm.expectRevert("Uniswap lookback too high");
new Aggor(
Expand All @@ -279,6 +301,7 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapQuoteToken,
decimals,
quoteDecimals,
type(uint32).max, // <- !
agreementDistance,
ageThreshold
Expand All @@ -290,6 +313,7 @@ contract AggorTest is Test {
uniswapBaseToken,
uniswapQuoteToken,
decimals,
quoteDecimals,
type(uint32).max
);
}
Expand Down Expand Up @@ -1048,6 +1072,7 @@ contract Aggor_VerifyTwapConfig is Aggor {
address uniswapBaseToken_,
address uniswapQuoteToken_,
uint8 uniswapBaseTokenDecimals_,
uint8 uniswapQuoteTokenDecimals_,
uint32 uniswapLookback_,
uint128 agreementDistance_,
uint32 ageThreshold_
Expand All @@ -1061,6 +1086,7 @@ contract Aggor_VerifyTwapConfig is Aggor {
uniswapBaseToken_,
uniswapQuoteToken_,
uniswapBaseTokenDecimals_,
uniswapQuoteTokenDecimals_,
uniswapLookback_,
agreementDistance_,
ageThreshold_
Expand All @@ -1072,13 +1098,15 @@ contract Aggor_VerifyTwapConfig is Aggor {
address uniswapBaseToken_,
address uniswapQuoteToken_,
uint8 uniswapBaseTokenDecimals_,
uint8 uniswapQuoteTokenDecimals_,
uint32 uniswapLookback_
) public view {
_verifyTwapConfig(
uniswapPool_,
uniswapBaseToken_,
uniswapQuoteToken_,
uniswapBaseTokenDecimals_,
uniswapQuoteTokenDecimals_,
uniswapLookback_
);
}
Expand Down
Loading

0 comments on commit 54087c8

Please sign in to comment.