Skip to content

Commit 54087c8

Browse files
committed
fix: Adds decimal scaling to _readTwap
1 parent a193343 commit 54087c8

File tree

7 files changed

+160
-10
lines changed

7 files changed

+160
-10
lines changed

audits/[email protected]

4.08 MB
Binary file not shown.

audits/[email protected]

518 KB
Binary file not shown.

script/Aggor.s.sol

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ contract AggorScript is Script {
2525
address uniswapBaseToken,
2626
address uniswapQuoteToken,
2727
uint8 uniswapBaseTokenDecimals,
28+
uint8 uniswapQuoteTokenDecimals,
2829
uint32 uniswapLookback,
2930
uint128 agreementDistance,
3031
uint32 ageThreshold
@@ -40,6 +41,7 @@ contract AggorScript is Script {
4041
uniswapBaseToken,
4142
uniswapQuoteToken,
4243
uniswapBaseTokenDecimals,
44+
uniswapQuoteTokenDecimals,
4345
uniswapLookback,
4446
agreementDistance,
4547
ageThreshold

src/Aggor.sol

+22-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {LibMedian} from "./libs/LibMedian.sol";
1919

2020
/**
2121
* @title Aggor
22-
* @custom:version v1.0.0
22+
* @custom:version v1.0.1
2323
*
2424
* @notice Oracle aggregator distributing trust among different oracle providers
2525
*
@@ -74,6 +74,8 @@ contract Aggor is IAggor, IToll, Auth {
7474
/// @inheritdoc IAggor
7575
uint8 public immutable uniswapBaseTokenDecimals;
7676
/// @inheritdoc IAggor
77+
uint8 public immutable uniswapQuoteTokenDecimals;
78+
/// @inheritdoc IAggor
7779
uint32 public immutable uniswapLookback;
7880

7981
// -- Mutable Configurations --
@@ -103,6 +105,7 @@ contract Aggor is IAggor, IToll, Auth {
103105
address uniswapBaseToken_,
104106
address uniswapQuoteToken_,
105107
uint8 uniswapBaseTokenDecimals_,
108+
uint8 uniswapQuoteTokenDecimals_,
106109
uint32 uniswapLookback_,
107110
uint128 agreementDistance_,
108111
uint32 ageThreshold_
@@ -113,6 +116,7 @@ contract Aggor is IAggor, IToll, Auth {
113116
uniswapBaseToken_,
114117
uniswapQuoteToken_,
115118
uniswapBaseTokenDecimals_,
119+
uniswapQuoteTokenDecimals_,
116120
uniswapLookback_
117121
);
118122

@@ -124,6 +128,7 @@ contract Aggor is IAggor, IToll, Auth {
124128
uniswapBaseToken = uniswapBaseToken_;
125129
uniswapQuoteToken = uniswapQuoteToken_;
126130
uniswapBaseTokenDecimals = uniswapBaseTokenDecimals_;
131+
uniswapQuoteTokenDecimals = uniswapQuoteTokenDecimals_;
127132
uniswapLookback = uniswapLookback_;
128133

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

166-
// Verify base token's decimals.
172+
// Verify token decimals.
167173
require(
168174
uniswapBaseTokenDecimals_ == IERC20(uniswapBaseToken_).decimals(),
169175
"Uniswap base token decimals mismatch"
@@ -172,6 +178,10 @@ contract Aggor is IAggor, IToll, Auth {
172178
uniswapBaseTokenDecimals_ <= _MAX_UNISWAP_BASE_DECIMALS,
173179
"Uniswap base token decimals too high"
174180
);
181+
require(
182+
uniswapQuoteTokenDecimals_ == IERC20(uniswapQuoteToken_).decimals(),
183+
"Uniswap quote token decimals mismatch"
184+
);
175185

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

351+
if (uniswapQuoteTokenDecimals <= decimals) {
352+
// Scale up
353+
twap *= 10 ** (decimals - uniswapQuoteTokenDecimals);
354+
} else {
355+
// Scale down
356+
twap /= 10 ** (uniswapQuoteTokenDecimals - decimals);
357+
}
358+
341359
if (twap <= type(uint128).max) {
342360
return (true, uint128(twap));
343361
} else {
@@ -488,6 +506,7 @@ contract Aggor_BASE_QUOTE_COUNTER is Aggor {
488506
address uniswapBaseToken_,
489507
address uniswapQuoteToken_,
490508
uint8 uniswapBaseDec_,
509+
uint8 uniswapQuoteDec_,
491510
uint32 uniswapLookback_,
492511
uint128 agreementDistance_,
493512
uint32 ageThreshold_
@@ -501,6 +520,7 @@ contract Aggor_BASE_QUOTE_COUNTER is Aggor {
501520
uniswapBaseToken_,
502521
uniswapQuoteToken_,
503522
uniswapBaseDec_,
523+
uniswapQuoteDec_,
504524
uniswapLookback_,
505525
agreementDistance_,
506526
ageThreshold_

src/IAggor.sol

+7
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ interface IAggor {
103103
view
104104
returns (uint8 baseTokenDecimals);
105105

106+
/// @notice Returns the Uniswap pool's quote token's decimals.
107+
/// @return quoteTokenDecimals The Uniswap pool's quote token's decimals.
108+
function uniswapQuoteTokenDecimals()
109+
external
110+
view
111+
returns (uint8 quoteTokenDecimals);
112+
106113
/// @notice Returns the time in seconds to use as lookback for Uniswap Twap
107114
/// oracle.
108115
/// @return lookback The time in seconds to use as lookback.

test/Aggor.t.sol

+30-2
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ contract AggorTest is Test {
3434
// Twap Provider:
3535
address uniswapPool = address(new UniswapPoolMock());
3636
address uniswapBaseToken = address(new ERC20Mock("base", "base", 18));
37-
address uniswapQuoteToken = address(new ERC20Mock("quote", "quote", 18));
37+
address uniswapQuoteToken = address(new ERC20Mock("quote", "quote", 6));
3838
uint32 uniswapLookback = 1 days;
3939

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

4343
// Configurations:
4444
uint128 agreementDistance = 9e17; // = 0.9e18 = 10%
@@ -58,6 +58,7 @@ contract AggorTest is Test {
5858
uniswapBaseToken,
5959
uniswapQuoteToken,
6060
IERC20(uniswapBaseToken).decimals(),
61+
IERC20(uniswapQuoteToken).decimals(),
6162
uniswapLookback,
6263
agreementDistance,
6364
ageThreshold
@@ -74,6 +75,7 @@ contract AggorTest is Test {
7475
uniswapBaseToken,
7576
uniswapQuoteToken,
7677
IERC20(uniswapBaseToken).decimals(),
78+
IERC20(uniswapQuoteToken).decimals(),
7779
uniswapLookback,
7880
agreementDistance,
7981
ageThreshold
@@ -85,6 +87,7 @@ contract AggorTest is Test {
8587
uniswapBaseToken,
8688
uniswapQuoteToken,
8789
IERC20(uniswapBaseToken).decimals(),
90+
IERC20(uniswapQuoteToken).decimals(),
8891
uniswapLookback
8992
);
9093
}
@@ -93,6 +96,7 @@ contract AggorTest is Test {
9396

9497
function test_Deployment_FailsIf_UniswapPoolZeroAddress() public {
9598
uint8 decimals = IERC20(uniswapBaseToken).decimals();
99+
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();
96100

97101
vm.expectRevert("Uniswap pool must not be zero");
98102
new Aggor(
@@ -104,6 +108,7 @@ contract AggorTest is Test {
104108
uniswapBaseToken,
105109
uniswapQuoteToken,
106110
decimals,
111+
quoteDecimals,
107112
uniswapLookback,
108113
agreementDistance,
109114
ageThreshold
@@ -115,12 +120,14 @@ contract AggorTest is Test {
115120
uniswapBaseToken,
116121
uniswapQuoteToken,
117122
decimals,
123+
quoteDecimals,
118124
uniswapLookback
119125
);
120126
}
121127

122128
function test_Deployment_FailsIf_BaseTokenEqualsQuoteToken() public {
123129
uint8 decimals = IERC20(uniswapBaseToken).decimals();
130+
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();
124131

125132
vm.expectRevert("Uniswap tokens must not be equal");
126133
new Aggor(
@@ -132,6 +139,7 @@ contract AggorTest is Test {
132139
uniswapBaseToken,
133140
uniswapBaseToken, // <- !
134141
decimals,
142+
quoteDecimals,
135143
uniswapLookback,
136144
agreementDistance,
137145
ageThreshold
@@ -143,13 +151,15 @@ contract AggorTest is Test {
143151
uniswapBaseToken,
144152
uniswapBaseToken, // <- !
145153
decimals,
154+
quoteDecimals,
146155
uniswapLookback
147156
);
148157
}
149158

150159
function test_Deployment_FailsIf_BaseTokenNotPoolToken() public {
151160
address notPoolToken = address(new ERC20Mock("", "", 18));
152161
uint8 decimals = IERC20(notPoolToken).decimals();
162+
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();
153163

154164
vm.expectRevert("Uniswap base token mismatch");
155165
new Aggor(
@@ -161,6 +171,7 @@ contract AggorTest is Test {
161171
notPoolToken, // <- !
162172
uniswapQuoteToken,
163173
decimals,
174+
quoteDecimals,
164175
uniswapLookback,
165176
agreementDistance,
166177
ageThreshold
@@ -172,13 +183,15 @@ contract AggorTest is Test {
172183
notPoolToken, // <- !
173184
uniswapQuoteToken,
174185
decimals,
186+
quoteDecimals,
175187
uniswapLookback
176188
);
177189
}
178190

179191
function test_Deployment_FailsIf_QuoteTokenNotPoolToken() public {
180192
address notPoolToken = address(new ERC20Mock("", "", 18));
181193
uint8 decimals = IERC20(uniswapBaseToken).decimals();
194+
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();
182195

183196
vm.expectRevert("Uniswap quote token mismatch");
184197
new Aggor(
@@ -190,6 +203,7 @@ contract AggorTest is Test {
190203
uniswapBaseToken,
191204
notPoolToken, // <- !
192205
decimals,
206+
quoteDecimals,
193207
uniswapLookback,
194208
agreementDistance,
195209
ageThreshold
@@ -201,12 +215,14 @@ contract AggorTest is Test {
201215
uniswapBaseToken,
202216
notPoolToken, // <- !
203217
decimals,
218+
quoteDecimals,
204219
uniswapLookback
205220
);
206221
}
207222

208223
function test_Deployment_FailsIf_BaseTokenDecimalsWrong() public {
209224
uint8 decimals = IERC20(uniswapBaseToken).decimals();
225+
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();
210226

211227
vm.expectRevert("Uniswap base token decimals mismatch");
212228
new Aggor(
@@ -218,6 +234,7 @@ contract AggorTest is Test {
218234
uniswapBaseToken,
219235
uniswapQuoteToken,
220236
decimals + 1, // <- !
237+
quoteDecimals,
221238
uniswapLookback,
222239
agreementDistance,
223240
ageThreshold
@@ -229,6 +246,7 @@ contract AggorTest is Test {
229246
uniswapBaseToken,
230247
uniswapQuoteToken,
231248
decimals + 1, // <- !
249+
quoteDecimals,
232250
uniswapLookback
233251
);
234252
}
@@ -237,6 +255,7 @@ contract AggorTest is Test {
237255
) public {
238256
uniswapBaseToken = address(new ERC20Mock("base", "base", 100)); // <- !
239257
uint8 decimals = IERC20(uniswapBaseToken).decimals();
258+
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();
240259

241260
UniswapPoolMock(uniswapPool).setToken0(uniswapBaseToken);
242261

@@ -250,6 +269,7 @@ contract AggorTest is Test {
250269
uniswapBaseToken,
251270
uniswapQuoteToken,
252271
decimals,
272+
quoteDecimals,
253273
uniswapLookback,
254274
agreementDistance,
255275
ageThreshold
@@ -261,13 +281,15 @@ contract AggorTest is Test {
261281
uniswapBaseToken,
262282
uniswapQuoteToken,
263283
decimals,
284+
quoteDecimals,
264285
uniswapLookback
265286
);
266287
}
267288

268289
function test_Deployment_FailsIf_UniswapLookbackBiggerThanOldestObservation(
269290
) public {
270291
uint8 decimals = IERC20(uniswapBaseToken).decimals();
292+
uint8 quoteDecimals = IERC20(uniswapQuoteToken).decimals();
271293

272294
vm.expectRevert("Uniswap lookback too high");
273295
new Aggor(
@@ -279,6 +301,7 @@ contract AggorTest is Test {
279301
uniswapBaseToken,
280302
uniswapQuoteToken,
281303
decimals,
304+
quoteDecimals,
282305
type(uint32).max, // <- !
283306
agreementDistance,
284307
ageThreshold
@@ -290,6 +313,7 @@ contract AggorTest is Test {
290313
uniswapBaseToken,
291314
uniswapQuoteToken,
292315
decimals,
316+
quoteDecimals,
293317
type(uint32).max
294318
);
295319
}
@@ -1048,6 +1072,7 @@ contract Aggor_VerifyTwapConfig is Aggor {
10481072
address uniswapBaseToken_,
10491073
address uniswapQuoteToken_,
10501074
uint8 uniswapBaseTokenDecimals_,
1075+
uint8 uniswapQuoteTokenDecimals_,
10511076
uint32 uniswapLookback_,
10521077
uint128 agreementDistance_,
10531078
uint32 ageThreshold_
@@ -1061,6 +1086,7 @@ contract Aggor_VerifyTwapConfig is Aggor {
10611086
uniswapBaseToken_,
10621087
uniswapQuoteToken_,
10631088
uniswapBaseTokenDecimals_,
1089+
uniswapQuoteTokenDecimals_,
10641090
uniswapLookback_,
10651091
agreementDistance_,
10661092
ageThreshold_
@@ -1072,13 +1098,15 @@ contract Aggor_VerifyTwapConfig is Aggor {
10721098
address uniswapBaseToken_,
10731099
address uniswapQuoteToken_,
10741100
uint8 uniswapBaseTokenDecimals_,
1101+
uint8 uniswapQuoteTokenDecimals_,
10751102
uint32 uniswapLookback_
10761103
) public view {
10771104
_verifyTwapConfig(
10781105
uniswapPool_,
10791106
uniswapBaseToken_,
10801107
uniswapQuoteToken_,
10811108
uniswapBaseTokenDecimals_,
1109+
uniswapQuoteTokenDecimals_,
10821110
uniswapLookback_
10831111
);
10841112
}

0 commit comments

Comments
 (0)