Skip to content

Commit f6c8988

Browse files
hivtushokcodenutt
authored andcommitted
feat(custom-redstone): Store feed decimals
1 parent 11044cc commit f6c8988

File tree

2 files changed

+31
-19
lines changed

2 files changed

+31
-19
lines changed

src/oracles/providers/CustomRedStoneOracleAdapter.sol

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { Roles } from "src/libs/Roles.sol";
2525
* ref: https://docs.redstone.finance/docs/get-started/models/redstone-pull/#manual-payload
2626
*/
2727
contract CustomRedStoneOracleAdapter is PrimaryProdDataServiceConsumerBase, SystemComponent, SecurityBase {
28-
event FeedIdRegistered(bytes32 indexed feedId, address indexed tokenAddress);
28+
event FeedIdRegistered(bytes32 indexed feedId, address indexed tokenAddress, bool ethQuoted, uint256 feedDecimals);
2929
event FeedIdRemoved(bytes32 indexed feedId);
3030

3131
error TokenNotRegistered(bytes32 feedId, address tokenAddress);
@@ -36,6 +36,7 @@ contract CustomRedStoneOracleAdapter is PrimaryProdDataServiceConsumerBase, Syst
3636
struct FeedId {
3737
address tokenAddress;
3838
bool ethQuoted;
39+
uint256 feedDecimals;
3940
}
4041

4142
/// @notice Mapping between a Redstone feedId and token address and if the price is quoted in ETH
@@ -99,7 +100,14 @@ contract CustomRedStoneOracleAdapter is PrimaryProdDataServiceConsumerBase, Syst
99100

100101
// Validate the price
101102
Errors.verifyNotZero(values[i], "baseToken price");
102-
values[i] = values[i] * 10 ** 10; // Convert to ETH decimals since Redstone uses 8 decimals
103+
104+
// Adjust the price from the feed decimals to 18 decimals
105+
uint256 feedDecimals = feedId.feedDecimals;
106+
if (feedDecimals < 18) {
107+
values[i] = values[i] * 10 ** (18 - feedDecimals);
108+
} else if (feedDecimals > 18) {
109+
values[i] = values[i] / 10 ** (feedDecimals - 18);
110+
}
103111

104112
// Convert to ETH if the data feed price is not quoted in ETH
105113
if (!feedId.ethQuoted) {
@@ -133,12 +141,13 @@ contract CustomRedStoneOracleAdapter is PrimaryProdDataServiceConsumerBase, Syst
133141
function registerFeedId(
134142
bytes32 feedId,
135143
address tokenAddress,
136-
bool ethQuoted
144+
bool ethQuoted,
145+
uint256 feedDecimals
137146
) external hasRole(Roles.ORACLE_MANAGER) {
138147
Errors.verifyNotZero(feedId, "feedId");
139148
Errors.verifyNotZero(address(tokenAddress), "tokenAddress");
140-
registeredFeedIds[feedId] = FeedId(tokenAddress, ethQuoted);
141-
emit FeedIdRegistered(feedId, tokenAddress);
149+
registeredFeedIds[feedId] = FeedId(tokenAddress, ethQuoted, feedDecimals);
150+
emit FeedIdRegistered(feedId, tokenAddress, ethQuoted, feedDecimals);
142151
}
143152

144153
/// @notice Removes a mapping between a Redstone feedId and token address

test/oracles/providers/CustomRedStoneOracleAdapter.t.sol

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ contract CustomRedStoneOracleAdapterTest is Test {
5858

5959
///@dev Get the Redstone payload snapshot for a given feedId
6060
///@return data The real Redstone payload from their API
61-
function getRedstonePayload(bytes32 feedId) internal pure returns (bytes memory data) {
61+
function getRedstonePayload(
62+
bytes32 feedId
63+
) internal pure returns (bytes memory data) {
6264
//solhint-disable max-line-length
6365
if (feedId == bytes32("pxETH/ETH")) {
6466
data =
@@ -130,12 +132,13 @@ contract RegisterFeedId is CustomRedStoneOracleAdapterTest {
130132
bytes32 feedId = bytes32("TEST");
131133
address token = makeAddr("token");
132134

133-
redstoneAdapter.registerFeedId(feedId, token, true);
135+
redstoneAdapter.registerFeedId(feedId, token, true, 8);
134136

135-
(address tokenAddress, bool ethQuoted) = redstoneAdapter.registeredFeedIds(feedId);
137+
(address tokenAddress, bool ethQuoted, uint256 feedDecimals) = redstoneAdapter.registeredFeedIds(feedId);
136138

137139
assertEq(tokenAddress, token);
138140
assertEq(ethQuoted, true);
141+
assertEq(feedDecimals, 8);
139142
}
140143

141144
function testRegisterFeedIdRevertsIfNoAccess() public {
@@ -144,7 +147,7 @@ contract RegisterFeedId is CustomRedStoneOracleAdapterTest {
144147

145148
vm.prank(RANDOM);
146149
vm.expectRevert(abi.encodeWithSignature("AccessDenied()"));
147-
redstoneAdapter.registerFeedId(feedId, token, true);
150+
redstoneAdapter.registerFeedId(feedId, token, true, 8);
148151
}
149152
}
150153

@@ -153,18 +156,18 @@ contract RemoveFeedId is CustomRedStoneOracleAdapterTest {
153156
bytes32 feedId = bytes32("TEST");
154157
address token = makeAddr("token");
155158

156-
redstoneAdapter.registerFeedId(feedId, token, true);
159+
redstoneAdapter.registerFeedId(feedId, token, true, 8);
157160
redstoneAdapter.removeFeedId(feedId);
158161

159-
(address tokenAddress,) = redstoneAdapter.registeredFeedIds(feedId);
162+
(address tokenAddress,,) = redstoneAdapter.registeredFeedIds(feedId);
160163
assertEq(tokenAddress, address(0));
161164
}
162165

163166
function test_RemoveFeedIdRevertsIfNoAccess() public {
164167
bytes32 feedId = bytes32("TEST");
165168
address token = makeAddr("token");
166169

167-
redstoneAdapter.registerFeedId(feedId, token, true);
170+
redstoneAdapter.registerFeedId(feedId, token, true, 8);
168171

169172
vm.prank(RANDOM);
170173
vm.expectRevert(abi.encodeWithSignature("AccessDenied()"));
@@ -182,7 +185,7 @@ contract UpdatePriceWithFeedId is CustomRedStoneOracleAdapterTest {
182185
bytes memory encodedFunction = abi.encodeWithSignature("updatePriceWithFeedId(bytes32[])", feedIds);
183186
bytes memory encodedFunctionWithRedstonePayload = abi.encodePacked(encodedFunction, redstonePayload);
184187

185-
redstoneAdapter.registerFeedId(feedIds[0], PXETH_MAINNET, true);
188+
redstoneAdapter.registerFeedId(feedIds[0], PXETH_MAINNET, true, 8);
186189

187190
vm.prank(TREASURY);
188191
accessController.grantRole(Roles.CUSTOM_ORACLE_EXECUTOR, address(redstoneAdapter));
@@ -214,7 +217,7 @@ contract UpdatePriceWithFeedId is CustomRedStoneOracleAdapterTest {
214217
function test_UpdatePriceWithFeedIdRevertsIfFeedIdIsNotExisting() public {
215218
bytes32[] memory feedIds = new bytes32[](1);
216219
feedIds[0] = bytes32("someRandomFeedIdThatDoesNotExist");
217-
redstoneAdapter.registerFeedId(feedIds[0], PXETH_MAINNET, true);
220+
redstoneAdapter.registerFeedId(feedIds[0], PXETH_MAINNET, true, 8);
218221

219222
bytes memory redstonePayload = getRedstonePayload(feedIds[0]);
220223
bytes memory encodedFunction = abi.encodeWithSignature("updatePriceWithFeedId(bytes32[])", feedIds);
@@ -232,7 +235,7 @@ contract UpdatePriceWithFeedId is CustomRedStoneOracleAdapterTest {
232235
function test_UpdatesPriceWithUsdNominatedFeedId() public {
233236
bytes32[] memory feedIds = new bytes32[](1);
234237
feedIds[0] = bytes32("CRV");
235-
redstoneAdapter.registerFeedId(feedIds[0], CRV_MAINNET, false);
238+
redstoneAdapter.registerFeedId(feedIds[0], CRV_MAINNET, false, 8);
236239

237240
address[] memory tokenAddresses = new address[](1);
238241
tokenAddresses[0] = CRV_MAINNET;
@@ -268,7 +271,7 @@ contract UpdatePriceWithFeedId is CustomRedStoneOracleAdapterTest {
268271
function test_UpdatesPriceWithSingleFeedId() public {
269272
bytes32[] memory feedIds = new bytes32[](1);
270273
feedIds[0] = bytes32("pxETH/ETH");
271-
redstoneAdapter.registerFeedId(feedIds[0], PXETH_MAINNET, true);
274+
redstoneAdapter.registerFeedId(feedIds[0], PXETH_MAINNET, true, 8);
272275

273276
bytes memory redstonePayload = getRedstonePayload(feedIds[0]);
274277
bytes memory encodedFunction = abi.encodeWithSignature("updatePriceWithFeedId(bytes32[])", feedIds);
@@ -316,9 +319,9 @@ contract UpdatePriceWithFeedId is CustomRedStoneOracleAdapterTest {
316319
bytes memory encodedFunction = abi.encodeWithSignature("updatePriceWithFeedId(bytes32[])", feedIds);
317320
bytes memory encodedFunctionWithRedstonePayload = abi.encodePacked(encodedFunction, redstonePayload);
318321

319-
redstoneAdapter.registerFeedId(feedIds[0], EZETH_MAINNET, true);
320-
redstoneAdapter.registerFeedId(feedIds[1], WSTETH_MAINNET, true);
321-
redstoneAdapter.registerFeedId(feedIds[2], RETH_MAINNET, true);
322+
redstoneAdapter.registerFeedId(feedIds[0], EZETH_MAINNET, true, 8);
323+
redstoneAdapter.registerFeedId(feedIds[1], WSTETH_MAINNET, true, 8);
324+
redstoneAdapter.registerFeedId(feedIds[2], RETH_MAINNET, true, 8);
322325

323326
vm.prank(TREASURY);
324327
accessController.grantRole(Roles.CUSTOM_ORACLE_EXECUTOR, address(redstoneAdapter));

0 commit comments

Comments
 (0)