Skip to content

Commit 2779982

Browse files
committed
chore(evm) Add combine Pricefeeds
1 parent fb15906 commit 2779982

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

target_chains/ethereum/contracts/forge-test/utils/PythTestUtils.t.sol

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,4 +399,66 @@ contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
399399
// 0.00001 and we are targetDecimals is 2.
400400
assertEq(PythUtils.convertToUint(100, -5, 2), 0);
401401
}
402+
403+
function testCombinePrices() public {
404+
// Test case 1: Basic price combination (ETH/USD / BTC/USD = ETH/BTC)
405+
PythStructs.Price memory ethUsd = PythStructs.Price({
406+
price: 99875005,
407+
conf: 10,
408+
expo: -8,
409+
publishTime: block.timestamp
410+
});
411+
412+
PythStructs.Price memory btcUsd = PythStructs.Price({
413+
price: 206362333702,
414+
conf: 100,
415+
expo: -8,
416+
publishTime: block.timestamp
417+
});
418+
419+
(int64 price, int32 expo) = PythUtils.combinePrices(ethUsd, btcUsd);
420+
assertApproxEqRel(price, 2016367433623696, 9e17);
421+
assertEq(expo, -18);
422+
423+
// // Test case 2: Different exponents
424+
// PythStructs.Price memory smallPrice = PythStructs.Price({
425+
// price: 100, // $0.01
426+
// conf: 1,
427+
// expo: -4, // 4 decimals
428+
// publishTime: block.timestamp
429+
// });
430+
431+
// PythStructs.Price memory largePrice = PythStructs.Price({
432+
// price: 1000, // $10
433+
// conf: 10,
434+
// expo: -2, // 2 decimals
435+
// publishTime: block.timestamp
436+
// });
437+
438+
// (price, expo) = PythUtils.combinePrices(smallPrice, largePrice, -2);
439+
// assertEq(price, 1); // 0.01
440+
// assertEq(expo, -2);
441+
442+
// // Test case 3: Revert on negative prices
443+
// PythStructs.Price memory negativePrice = PythStructs.Price({
444+
// price: -100,
445+
// conf: 10,
446+
// expo: -2,
447+
// publishTime: block.timestamp
448+
// });
449+
450+
// vm.expectRevert();
451+
// PythUtils.combinePrices(negativePrice, btcUsd, -2);
452+
453+
// // Test case 4: Revert on positive exponents
454+
// PythStructs.Price memory invalidExpo = PythStructs.Price({
455+
// price: 100,
456+
// conf: 10,
457+
// expo: 2,
458+
// publishTime: block.timestamp
459+
// });
460+
461+
// vm.expectRevert();
462+
// PythUtils.combinePrices(ethUsd, invalidExpo, -2);
463+
}
402464
}

target_chains/ethereum/sdk/solidity/PythUtils.sol

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.0;
33

44
import "./PythStructs.sol";
5-
5+
import "forge-std/console.sol";
66
library PythUtils {
77
/// @notice Converts a Pyth price to a uint256 with a target number of decimals
88
/// @param price The Pyth price
@@ -33,4 +33,51 @@ library PythUtils {
3333
10 ** uint32(priceDecimals - targetDecimals);
3434
}
3535
}
36+
37+
/// @notice Combines two prices to get a cross-rate
38+
/// @param price1 The first price (a/b)
39+
/// @param price2 The second price (c/b)
40+
/// @return combinedPrice The combined price (a/c)
41+
/// @return expo The exponent of the combined price
42+
/// @dev This function will revert if either price is negative or if the exponents are invalid
43+
function combinePrices(
44+
PythStructs.Price memory price1,
45+
PythStructs.Price memory price2
46+
) public pure returns (int64 combinedPrice, int32 expo) {
47+
if (price1.price < 0 || price2.price < 0 || price1.expo > 0 || price2.expo > 0 || price1.expo < -255 || price2.expo < -255) {
48+
revert();
49+
}
50+
51+
uint8 p1Decimals = uint8(uint32(-1 * price1.expo));
52+
uint8 p2Decimals = uint8(uint32(-1 * price2.expo));
53+
54+
uint256 p1;
55+
uint256 p2;
56+
57+
if (p1Decimals >= p2Decimals) {
58+
p2 = convertToUint(price2.price, price2.expo, p1Decimals);
59+
p1 = uint256(uint64(price1.price));
60+
} else {
61+
p1 = convertToUint(price1.price, price1.expo, p2Decimals);
62+
p2 = uint256(uint64(price2.price));
63+
}
64+
65+
66+
console.log("p1", p1);
67+
console.log("p2", p2);
68+
69+
70+
// Calculate the combined price
71+
uint256 combined = (p1 * 10**18) / p2; // Multiply by 10^18 to maintain precision
72+
console.log("combined", combined);
73+
// Calculate the new exponent
74+
int32 newExpo = -18;
75+
console.log("newExpo", newExpo);
76+
// Check if the combined price fits in int64
77+
if (combined > uint256(uint64(type(int64).max))) {
78+
revert();
79+
}
80+
81+
return (int64(uint64(combined)), newExpo);
82+
}
3683
}

0 commit comments

Comments
 (0)