Skip to content

Commit d5c61c2

Browse files
committed
add benchmarking for variable feeds
1 parent d24c070 commit d5c61c2

File tree

2 files changed

+139
-21
lines changed

2 files changed

+139
-21
lines changed

target_chains/ethereum/contracts/forge-test/PulseGasBenchmark.t.sol

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import "../contracts/pulse/PulseState.sol";
1010
import "../contracts/pulse/PulseEvents.sol";
1111
import "../contracts/pulse/PulseErrors.sol";
1212
import "./utils/PulseTestUtils.t.sol";
13-
13+
import {console} from "forge-std/console.sol";
1414
contract PulseGasBenchmark is Test, PulseTestUtils {
1515
ERC1967Proxy public proxy;
1616
PulseUpgradeable public pulse;
@@ -95,6 +95,79 @@ contract PulseGasBenchmark is Test, PulseTestUtils {
9595
priceIds
9696
);
9797
}
98+
99+
// Runs benchmark with feeds and returns the gas usage breakdown
100+
function _runBenchmarkWithFeedsAndGasTracking(
101+
uint256 numFeeds
102+
) internal returns (uint256 requestGas, uint256 executeGas) {
103+
uint64 timestamp = SafeCast.toUint64(block.timestamp);
104+
bytes32[] memory priceIds = createPriceIds(numFeeds);
105+
106+
uint32 callbackGasLimit = 100000;
107+
uint96 totalFee = pulse.getFee(
108+
defaultProvider,
109+
callbackGasLimit,
110+
priceIds
111+
);
112+
vm.deal(address(consumer), 1 ether);
113+
114+
// Measure gas for request
115+
uint256 gasBefore = gasleft();
116+
vm.prank(address(consumer));
117+
uint64 sequenceNumber = pulse.requestPriceUpdatesWithCallback{
118+
value: totalFee
119+
}(defaultProvider, timestamp, priceIds, callbackGasLimit);
120+
requestGas = gasBefore - gasleft();
121+
122+
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds(
123+
timestamp,
124+
numFeeds
125+
);
126+
mockParsePriceFeedUpdates(pyth, priceFeeds);
127+
bytes[] memory updateData = createMockUpdateData(priceFeeds);
128+
129+
// Measure gas for execute
130+
gasBefore = gasleft();
131+
pulse.executeCallback(
132+
defaultProvider,
133+
sequenceNumber,
134+
updateData,
135+
priceIds
136+
);
137+
executeGas = gasBefore - gasleft();
138+
}
139+
140+
function testGasBreakdownByFeeds() public {
141+
uint256[] memory feedCounts = new uint256[](5);
142+
feedCounts[0] = 1;
143+
feedCounts[1] = 2;
144+
feedCounts[2] = 4;
145+
feedCounts[3] = 8;
146+
feedCounts[4] = 10;
147+
148+
console.log("=== Gas Usage Breakdown ===");
149+
for (uint256 i = 0; i < feedCounts.length; i++) {
150+
console.log("--> Feeds: %s", vm.toString(feedCounts[i]));
151+
(
152+
uint256 requestGas,
153+
uint256 executeCallbackGas
154+
) = _runBenchmarkWithFeedsAndGasTracking(feedCounts[i]);
155+
156+
string memory requestGasStr = vm.toString(requestGas);
157+
string memory executeCallbackGasStr = vm.toString(
158+
executeCallbackGas
159+
);
160+
string memory totalGasStr = vm.toString(
161+
requestGas + executeCallbackGas
162+
);
163+
console.log(
164+
"Request gas: %s | Callback gas: %s | Total gas: %s",
165+
requestGasStr,
166+
executeCallbackGasStr,
167+
totalGasStr
168+
);
169+
}
170+
}
98171
}
99172

100173
// A simple consumer that does nothing with the price updates.

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

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@ abstract contract PulseTestUtils is Test {
1212
0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43;
1313
bytes32 constant ETH_PRICE_FEED_ID =
1414
0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace;
15+
bytes32 constant SOL_PRICE_FEED_ID =
16+
0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d;
17+
bytes32 constant AVAX_PRICE_FEED_ID =
18+
0x93da3352f9f1d105fdfe4971cfa80e9dd777bfc5d0f683ebb6e1294b92137bb7;
19+
bytes32 constant MELANIA_PRICE_FEED_ID =
20+
0x8fef7d52c7f4e3a6258d663f9d27e64a1b6fd95ab5f7d545dbf9a515353d0064;
21+
bytes32 constant PYTH_PRICE_FEED_ID =
22+
0x0bbf28e9a841a1cc788f6a361b17ca072d0ea3098a1e5df1c3922d06719579ff;
23+
bytes32 constant UNI_PRICE_FEED_ID =
24+
0x78d185a741d07edb3412b09008b7c5cfb9bbbd7d568bf00ba737b456ba171501;
25+
bytes32 constant AAVE_PRICE_FEED_ID =
26+
0x2b9ab1e972a281585084148ba1389800799bd4be63b957507db1349314e47445;
27+
bytes32 constant DOGE_PRICE_FEED_ID =
28+
0xdcef50dd0a4cd2dcc17e45df1676dcb336a11a61c69df7a0299b0150c672d25c;
29+
bytes32 constant ADA_PRICE_FEED_ID =
30+
0x2a01deaec9e51a579277b34b122399984d0bbf57e2458a7e42fecd2829867a0d;
1531

1632
// Price feed constants
1733
int8 constant MOCK_PRICE_FEED_EXPO = -8;
@@ -25,33 +41,58 @@ abstract contract PulseTestUtils is Test {
2541

2642
uint32 constant CALLBACK_GAS_LIMIT = 1_000_000;
2743

28-
// Helper function to create price IDs array
44+
// Helper function to create price IDs array with default 2 feeds
2945
function createPriceIds() internal pure returns (bytes32[] memory) {
30-
bytes32[] memory priceIds = new bytes32[](2);
31-
priceIds[0] = BTC_PRICE_FEED_ID;
32-
priceIds[1] = ETH_PRICE_FEED_ID;
46+
return createPriceIds(2);
47+
}
48+
49+
// Helper function to create price IDs array with variable number of feeds
50+
function createPriceIds(
51+
uint256 numFeeds
52+
) internal pure returns (bytes32[] memory) {
53+
require(numFeeds <= 10, "Too many price feeds requested");
54+
bytes32[] memory priceIds = new bytes32[](numFeeds);
55+
56+
if (numFeeds > 0) priceIds[0] = BTC_PRICE_FEED_ID;
57+
if (numFeeds > 1) priceIds[1] = ETH_PRICE_FEED_ID;
58+
if (numFeeds > 2) priceIds[2] = SOL_PRICE_FEED_ID;
59+
if (numFeeds > 3) priceIds[3] = AVAX_PRICE_FEED_ID;
60+
if (numFeeds > 4) priceIds[4] = MELANIA_PRICE_FEED_ID;
61+
if (numFeeds > 5) priceIds[5] = PYTH_PRICE_FEED_ID;
62+
if (numFeeds > 6) priceIds[6] = UNI_PRICE_FEED_ID;
63+
if (numFeeds > 7) priceIds[7] = AAVE_PRICE_FEED_ID;
64+
if (numFeeds > 8) priceIds[8] = DOGE_PRICE_FEED_ID;
65+
if (numFeeds > 9) priceIds[9] = ADA_PRICE_FEED_ID;
66+
3367
return priceIds;
3468
}
3569

36-
// Helper function to create mock price feeds
70+
// Helper function to create mock price feeds with default 2 feeds
3771
function createMockPriceFeeds(
3872
uint256 publishTime
3973
) internal pure returns (PythStructs.PriceFeed[] memory) {
74+
return createMockPriceFeeds(publishTime, 2);
75+
}
76+
77+
// Helper function to create mock price feeds with variable number of feeds
78+
function createMockPriceFeeds(
79+
uint256 publishTime,
80+
uint256 numFeeds
81+
) internal pure returns (PythStructs.PriceFeed[] memory) {
82+
require(numFeeds <= 10, "Too many price feeds requested");
4083
PythStructs.PriceFeed[] memory priceFeeds = new PythStructs.PriceFeed[](
41-
2
84+
numFeeds
4285
);
4386

44-
priceFeeds[0].id = BTC_PRICE_FEED_ID;
45-
priceFeeds[0].price.price = MOCK_BTC_PRICE;
46-
priceFeeds[0].price.conf = MOCK_BTC_CONF;
47-
priceFeeds[0].price.expo = MOCK_PRICE_FEED_EXPO;
48-
priceFeeds[0].price.publishTime = publishTime;
87+
bytes32[] memory priceIds = createPriceIds(numFeeds);
4988

50-
priceFeeds[1].id = ETH_PRICE_FEED_ID;
51-
priceFeeds[1].price.price = MOCK_ETH_PRICE;
52-
priceFeeds[1].price.conf = MOCK_ETH_CONF;
53-
priceFeeds[1].price.expo = MOCK_PRICE_FEED_EXPO;
54-
priceFeeds[1].price.publishTime = publishTime;
89+
for (uint256 i = 0; i < numFeeds; i++) {
90+
priceFeeds[i].id = priceIds[i];
91+
priceFeeds[i].price.price = MOCK_BTC_PRICE; // Using BTC price for all feeds for simplicity
92+
priceFeeds[i].price.conf = MOCK_BTC_CONF; // Using BTC conf for all feeds for simplicity
93+
priceFeeds[i].price.expo = MOCK_PRICE_FEED_EXPO;
94+
priceFeeds[i].price.publishTime = publishTime;
95+
}
5596

5697
return priceFeeds;
5798
}
@@ -77,13 +118,17 @@ abstract contract PulseTestUtils is Test {
77118
);
78119
}
79120

80-
// Helper function to create mock update data
121+
// Helper function to create mock update data for variable feeds
81122
function createMockUpdateData(
82123
PythStructs.PriceFeed[] memory priceFeeds
83124
) internal pure returns (bytes[] memory) {
84-
bytes[] memory updateData = new bytes[](2);
85-
updateData[0] = abi.encode(priceFeeds[0]);
86-
updateData[1] = abi.encode(priceFeeds[1]);
125+
uint256 numFeeds = priceFeeds.length;
126+
bytes[] memory updateData = new bytes[](numFeeds);
127+
128+
for (uint256 i = 0; i < numFeeds; i++) {
129+
updateData[i] = abi.encode(priceFeeds[i]);
130+
}
131+
87132
return updateData;
88133
}
89134

0 commit comments

Comments
 (0)