Parcourir la source

feat: gas optimizations

Tejas Badadare il y a 8 mois
Parent
commit
5848112052

+ 12 - 12
target_chains/ethereum/contracts/contracts/pulse/IPulse.sol

@@ -55,7 +55,7 @@ interface IPulse is PulseEvents {
         address provider,
         uint64 publishTime,
         bytes32[] calldata priceIds,
-        uint256 callbackGasLimit
+        uint32 callbackGasLimit
     ) external payable returns (uint64 sequenceNumber);
 
     /**
@@ -80,7 +80,7 @@ interface IPulse is PulseEvents {
      * @dev This is a fixed fee per request that goes to the Pyth protocol, separate from gas costs
      * @return pythFeeInWei The base fee in wei that every request must pay
      */
-    function getPythFeeInWei() external view returns (uint128 pythFeeInWei);
+    function getPythFeeInWei() external view returns (uint96 pythFeeInWei);
 
     /**
      * @notice Calculates the total fee required for a price update request
@@ -92,9 +92,9 @@ interface IPulse is PulseEvents {
      */
     function getFee(
         address provider,
-        uint256 callbackGasLimit,
+        uint32 callbackGasLimit,
         bytes32[] calldata priceIds
-    ) external view returns (uint128 feeAmount);
+    ) external view returns (uint96 feeAmount);
 
     function getAccruedPythFees()
         external
@@ -116,16 +116,16 @@ interface IPulse is PulseEvents {
     function withdrawAsFeeManager(address provider, uint128 amount) external;
 
     function registerProvider(
-        uint128 baseFeeInWei,
-        uint128 feePerFeedInWei,
-        uint128 feePerGasInWei
+        uint96 baseFeeInWei,
+        uint96 feePerFeedInWei,
+        uint96 feePerGasInWei
     ) external;
 
     function setProviderFee(
         address provider,
-        uint128 newBaseFeeInWei,
-        uint128 newFeePerFeedInWei,
-        uint128 newFeePerGasInWei
+        uint96 newBaseFeeInWei,
+        uint96 newFeePerFeedInWei,
+        uint96 newFeePerGasInWei
     ) external;
 
     function getProviderInfo(
@@ -136,9 +136,9 @@ interface IPulse is PulseEvents {
 
     function setDefaultProvider(address provider) external;
 
-    function setExclusivityPeriod(uint256 periodSeconds) external;
+    function setExclusivityPeriod(uint32 periodSeconds) external;
 
-    function getExclusivityPeriod() external view returns (uint256);
+    function getExclusivityPeriod() external view returns (uint32);
 
     /**
      * @notice Gets the first N active requests

+ 50 - 36
target_chains/ethereum/contracts/contracts/pulse/Pulse.sol

@@ -11,11 +11,11 @@ import "./PulseErrors.sol";
 abstract contract Pulse is IPulse, PulseState {
     function _initialize(
         address admin,
-        uint128 pythFeeInWei,
+        uint96 pythFeeInWei,
         address pythAddress,
         address defaultProvider,
         bool prefillRequestStorage,
-        uint256 exclusivityPeriodSeconds
+        uint32 exclusivityPeriodSeconds
     ) internal {
         require(admin != address(0), "admin is zero address");
         require(pythAddress != address(0), "pyth is zero address");
@@ -44,11 +44,8 @@ abstract contract Pulse is IPulse, PulseState {
                 req.publishTime = 1;
                 req.callbackGasLimit = 1;
                 req.requester = address(1);
-                req.numPriceIds = 0;
-                // Pre-warm the priceIds array storage
-                for (uint8 j = 0; j < MAX_PRICE_IDS; j++) {
-                    req.priceIds[j] = bytes32(0);
-                }
+                // Initialize with empty array, no need to pre-warm with a fixed size anymore
+                delete req.priceIdPrefixes;
             }
         }
     }
@@ -58,7 +55,7 @@ abstract contract Pulse is IPulse, PulseState {
         address provider,
         uint64 publishTime,
         bytes32[] calldata priceIds,
-        uint256 callbackGasLimit
+        uint32 callbackGasLimit
     ) external payable override returns (uint64 requestSequenceNumber) {
         require(
             _state.providers[provider].isRegistered,
@@ -77,7 +74,7 @@ abstract contract Pulse is IPulse, PulseState {
         }
         requestSequenceNumber = _state.currentSequenceNumber++;
 
-        uint128 requiredFee = getFee(provider, callbackGasLimit, priceIds);
+        uint96 requiredFee = getFee(provider, callbackGasLimit, priceIds);
         if (msg.value < requiredFee) revert InsufficientFee();
 
         Request storage req = allocRequest(requestSequenceNumber);
@@ -85,13 +82,21 @@ abstract contract Pulse is IPulse, PulseState {
         req.publishTime = publishTime;
         req.callbackGasLimit = callbackGasLimit;
         req.requester = msg.sender;
-        req.numPriceIds = uint8(priceIds.length);
         req.provider = provider;
-        req.fee = SafeCast.toUint128(msg.value - _state.pythFeeInWei);
+        req.fee = SafeCast.toUint96(msg.value - _state.pythFeeInWei);
 
-        // Copy price IDs to storage
+        // Create array with the right size
+        req.priceIdPrefixes = new bytes8[](priceIds.length);
+
+        // Copy only the first 8 bytes of each price ID to storage
         for (uint8 i = 0; i < priceIds.length; i++) {
-            req.priceIds[i] = priceIds[i];
+            // Extract first 8 bytes of the price ID
+            bytes32 priceId = priceIds[i];
+            bytes8 prefix;
+            assembly {
+                prefix := priceId
+            }
+            req.priceIdPrefixes[i] = prefix;
         }
         _state.accruedFeesInWei += _state.pythFeeInWei;
 
@@ -119,12 +124,21 @@ abstract contract Pulse is IPulse, PulseState {
 
         // Verify priceIds match
         require(
-            priceIds.length == req.numPriceIds,
+            priceIds.length == req.priceIdPrefixes.length,
             "Price IDs length mismatch"
         );
-        for (uint8 i = 0; i < req.numPriceIds; i++) {
-            if (priceIds[i] != req.priceIds[i]) {
-                revert InvalidPriceIds(priceIds[i], req.priceIds[i]);
+        for (uint8 i = 0; i < req.priceIdPrefixes.length; i++) {
+            // Extract first 8 bytes of the provided price ID
+            bytes32 priceId = priceIds[i];
+            bytes8 prefix;
+            assembly {
+                prefix := priceId
+            }
+
+            // Compare with stored prefix
+            if (prefix != req.priceIdPrefixes[i]) {
+                // Now we can directly use the bytes8 prefix in the error
+                revert InvalidPriceIds(priceIds[i], req.priceIdPrefixes[i]);
             }
         }
 
@@ -222,31 +236,31 @@ abstract contract Pulse is IPulse, PulseState {
 
     function getFee(
         address provider,
-        uint256 callbackGasLimit,
+        uint32 callbackGasLimit,
         bytes32[] calldata priceIds
-    ) public view override returns (uint128 feeAmount) {
-        uint128 baseFee = _state.pythFeeInWei; // Fixed fee to Pyth
+    ) public view override returns (uint96 feeAmount) {
+        uint96 baseFee = _state.pythFeeInWei; // Fixed fee to Pyth
         // Note: The provider needs to set its fees to include the fee charged by the Pyth contract.
         // Ideally, we would be able to automatically compute the pyth fees from the priceIds, but the
         // fee computation on IPyth assumes it has the full updated data.
-        uint128 providerBaseFee = _state.providers[provider].baseFeeInWei;
-        uint128 providerFeedFee = SafeCast.toUint128(
+        uint96 providerBaseFee = _state.providers[provider].baseFeeInWei;
+        uint96 providerFeedFee = SafeCast.toUint96(
             priceIds.length * _state.providers[provider].feePerFeedInWei
         );
-        uint128 providerFeeInWei = _state.providers[provider].feePerGasInWei; // Provider's per-gas rate
+        uint96 providerFeeInWei = _state.providers[provider].feePerGasInWei; // Provider's per-gas rate
         uint256 gasFee = callbackGasLimit * providerFeeInWei; // Total provider fee based on gas
         feeAmount =
             baseFee +
             providerBaseFee +
             providerFeedFee +
-            SafeCast.toUint128(gasFee); // Total fee user needs to pay
+            SafeCast.toUint96(gasFee); // Total fee user needs to pay
     }
 
     function getPythFeeInWei()
         public
         view
         override
-        returns (uint128 pythFeeInWei)
+        returns (uint96 pythFeeInWei)
     {
         pythFeeInWei = _state.pythFeeInWei;
     }
@@ -367,9 +381,9 @@ abstract contract Pulse is IPulse, PulseState {
     }
 
     function registerProvider(
-        uint128 baseFeeInWei,
-        uint128 feePerFeedInWei,
-        uint128 feePerGasInWei
+        uint96 baseFeeInWei,
+        uint96 feePerFeedInWei,
+        uint96 feePerGasInWei
     ) external override {
         ProviderInfo storage provider = _state.providers[msg.sender];
         require(!provider.isRegistered, "Provider already registered");
@@ -382,9 +396,9 @@ abstract contract Pulse is IPulse, PulseState {
 
     function setProviderFee(
         address provider,
-        uint128 newBaseFeeInWei,
-        uint128 newFeePerFeedInWei,
-        uint128 newFeePerGasInWei
+        uint96 newBaseFeeInWei,
+        uint96 newFeePerFeedInWei,
+        uint96 newFeePerGasInWei
     ) external override {
         require(
             _state.providers[provider].isRegistered,
@@ -396,9 +410,9 @@ abstract contract Pulse is IPulse, PulseState {
             "Only provider or fee manager can invoke this method"
         );
 
-        uint128 oldBaseFee = _state.providers[provider].baseFeeInWei;
-        uint128 oldFeePerFeed = _state.providers[provider].feePerFeedInWei;
-        uint128 oldFeePerGas = _state.providers[provider].feePerGasInWei;
+        uint96 oldBaseFee = _state.providers[provider].baseFeeInWei;
+        uint96 oldFeePerFeed = _state.providers[provider].feePerFeedInWei;
+        uint96 oldFeePerGas = _state.providers[provider].feePerGasInWei;
         _state.providers[provider].baseFeeInWei = newBaseFeeInWei;
         _state.providers[provider].feePerFeedInWei = newFeePerFeedInWei;
         _state.providers[provider].feePerGasInWei = newFeePerGasInWei;
@@ -437,7 +451,7 @@ abstract contract Pulse is IPulse, PulseState {
         emit DefaultProviderUpdated(oldProvider, provider);
     }
 
-    function setExclusivityPeriod(uint256 periodSeconds) external override {
+    function setExclusivityPeriod(uint32 periodSeconds) external override {
         require(
             msg.sender == _state.admin,
             "Only admin can set exclusivity period"
@@ -447,7 +461,7 @@ abstract contract Pulse is IPulse, PulseState {
         emit ExclusivityPeriodUpdated(oldPeriod, periodSeconds);
     }
 
-    function getExclusivityPeriod() external view override returns (uint256) {
+    function getExclusivityPeriod() external view override returns (uint32) {
         return _state.exclusivityPeriodSeconds;
     }
 

+ 1 - 1
target_chains/ethereum/contracts/contracts/pulse/PulseErrors.sol

@@ -9,7 +9,7 @@ error InsufficientFee();
 error Unauthorized();
 error InvalidCallbackGas();
 error CallbackFailed();
-error InvalidPriceIds(bytes32 providedPriceIdsHash, bytes32 storedPriceIdsHash);
+error InvalidPriceIds(bytes32 providedPriceId, bytes8 storedPriceId);
 error InvalidCallbackGasLimit(uint256 requested, uint256 stored);
 error ExceedsMaxPrices(uint32 requested, uint32 maxAllowed);
 error TooManyPriceIds(uint256 provided, uint256 maximum);

+ 7 - 7
target_chains/ethereum/contracts/contracts/pulse/PulseEvents.sol

@@ -32,15 +32,15 @@ interface PulseEvents {
         address newFeeManager
     );
 
-    event ProviderRegistered(address indexed provider, uint128 feeInWei);
+    event ProviderRegistered(address indexed provider, uint96 feeInWei);
     event ProviderFeeUpdated(
         address indexed provider,
-        uint128 oldBaseFee,
-        uint128 oldFeePerFeed,
-        uint128 oldFeePerGas,
-        uint128 newBaseFee,
-        uint128 newFeePerFeed,
-        uint128 newFeePerGas
+        uint96 oldBaseFee,
+        uint96 oldFeePerFeed,
+        uint96 oldFeePerGas,
+        uint96 newBaseFee,
+        uint96 newFeePerFeed,
+        uint96 newFeePerGas
     );
     event DefaultProviderUpdated(address oldProvider, address newProvider);
 

+ 37 - 16
target_chains/ethereum/contracts/contracts/pulse/PulseState.sol

@@ -10,41 +10,62 @@ contract PulseState {
     uint8 public constant MAX_PRICE_IDS = 10;
 
     struct Request {
+        // Slot 1: 8 + 8 + 4 + 12 = 32 bytes
         uint64 sequenceNumber;
         uint64 publishTime;
-        // TODO: this is going to absolutely explode gas costs. Need to do something smarter here.
-        // possible solution is to hash the price ids and store the hash instead.
-        // The ids themselves can be retrieved from the event.
-        bytes32[MAX_PRICE_IDS] priceIds;
-        uint8 numPriceIds; // Actual number of price IDs used
-        uint256 callbackGasLimit;
+        uint32 callbackGasLimit;
+        uint96 fee;
+        // Slot 2: 20 + 12 = 32 bytes
         address requester;
+        // 12 bytes padding
+
+        // Slot 3: 20 + 12 = 32 bytes
         address provider;
-        uint128 fee;
+        // 12 bytes padding
+
+        // Dynamic array starts at its own slot
+        // Store only first 8 bytes of each price ID to save gas
+        bytes8[] priceIdPrefixes;
     }
 
     struct ProviderInfo {
-        uint128 baseFeeInWei;
-        uint128 feePerFeedInWei;
-        uint128 feePerGasInWei;
+        // Slot 1: 12 + 12 + 8 = 32 bytes
+        uint96 baseFeeInWei;
+        uint96 feePerFeedInWei;
+        // 8 bytes padding
+
+        // Slot 2: 12 + 16 + 4 = 32 bytes
+        uint96 feePerGasInWei;
         uint128 accruedFeesInWei;
+        // 4 bytes padding
+
+        // Slot 3: 20 + 1 + 11 = 32 bytes
         address feeManager;
         bool isRegistered;
+        // 11 bytes padding
     }
 
     struct State {
+        // Slot 1: 20 + 4 + 8 = 32 bytes
         address admin;
-        uint128 pythFeeInWei;
-        uint128 accruedFeesInWei;
-        address pyth;
+        uint32 exclusivityPeriodSeconds;
         uint64 currentSequenceNumber;
+        // Slot 2: 20 + 8 + 4 = 32 bytes
+        address pyth;
+        uint64 firstUnfulfilledSeq;
+        // 4 bytes padding
+
+        // Slot 3: 20 + 12 = 32 bytes
         address defaultProvider;
-        uint256 exclusivityPeriodSeconds;
+        uint96 pythFeeInWei;
+        // Slot 4: 16 + 16 = 32 bytes
+        uint128 accruedFeesInWei;
+        // 16 bytes padding
+
+        // These take their own slots regardless of ordering
         Request[NUM_REQUESTS] requests;
         mapping(bytes32 => Request) requestsOverflow;
         mapping(address => ProviderInfo) providers;
-        uint64 firstUnfulfilledSeq; // All sequences before this are fulfilled
     }
-
     State internal _state;
 }

+ 2 - 2
target_chains/ethereum/contracts/contracts/pulse/PulseUpgradeable.sol

@@ -21,11 +21,11 @@ contract PulseUpgradeable is
     function initialize(
         address owner,
         address admin,
-        uint128 pythFeeInWei,
+        uint96 pythFeeInWei,
         address pythAddress,
         address defaultProvider,
         bool prefillRequestStorage,
-        uint256 exclusivityPeriodSeconds
+        uint32 exclusivityPeriodSeconds
     ) external initializer {
         require(owner != address(0), "owner is zero address");
         require(admin != address(0), "admin is zero address");

+ 66 - 46
target_chains/ethereum/contracts/forge-test/Pulse.t.sol

@@ -12,6 +12,9 @@ import "../contracts/pulse/PulseState.sol";
 import "../contracts/pulse/PulseEvents.sol";
 import "../contracts/pulse/PulseErrors.sol";
 
+// Concrete implementation for testing
+contract ConcretePulseUpgradeable is PulseUpgradeable {}
+
 contract MockPulseConsumer is IPulseConsumer {
     address private _pulse;
     uint64 public lastSequenceNumber;
@@ -94,17 +97,17 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
     address public pyth;
     address public defaultProvider;
     // Constants
-    uint128 constant PYTH_FEE = 1 wei;
-    uint128 constant DEFAULT_PROVIDER_FEE_PER_GAS = 1 wei;
-    uint128 constant DEFAULT_PROVIDER_BASE_FEE = 1 wei;
-    uint128 constant DEFAULT_PROVIDER_FEE_PER_FEED = 10 wei;
+    uint96 constant PYTH_FEE = 1 wei;
+    uint96 constant DEFAULT_PROVIDER_FEE_PER_GAS = 1 wei;
+    uint96 constant DEFAULT_PROVIDER_BASE_FEE = 1 wei;
+    uint96 constant DEFAULT_PROVIDER_FEE_PER_FEED = 10 wei;
 
     function setUp() public {
         owner = address(1);
         admin = address(2);
         pyth = address(3);
         defaultProvider = address(4);
-        PulseUpgradeable _pulse = new PulseUpgradeable();
+        PulseUpgradeable _pulse = new ConcretePulseUpgradeable();
         proxy = new ERC1967Proxy(address(_pulse), "");
         pulse = PulseUpgradeable(address(proxy));
 
@@ -128,7 +131,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
 
     // Helper function to calculate total fee
     // FIXME: I think this helper probably needs to take some arguments.
-    function calculateTotalFee() internal view returns (uint128) {
+    function calculateTotalFee() internal view returns (uint96) {
         return
             pulse.getFee(defaultProvider, CALLBACK_GAS_LIMIT, createPriceIds());
     }
@@ -142,26 +145,28 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
 
         // Fund the consumer contract with enough ETH for higher gas price
         vm.deal(address(consumer), 1 ether);
-        uint128 totalFee = calculateTotalFee();
+        uint96 totalFee = calculateTotalFee();
 
         // Create the event data we expect to see
+        bytes8[] memory expectedPriceIdPrefixes = new bytes8[](2);
+        {
+            bytes32 priceId0 = priceIds[0];
+            bytes32 priceId1 = priceIds[1];
+            bytes8 prefix0;
+            bytes8 prefix1;
+            assembly {
+                prefix0 := priceId0
+                prefix1 := priceId1
+            }
+            expectedPriceIdPrefixes[0] = prefix0;
+            expectedPriceIdPrefixes[1] = prefix1;
+        }
+
         PulseState.Request memory expectedRequest = PulseState.Request({
             sequenceNumber: 1,
             publishTime: publishTime,
-            priceIds: [
-                priceIds[0],
-                priceIds[1],
-                bytes32(0), // Fill remaining slots with zero
-                bytes32(0),
-                bytes32(0),
-                bytes32(0),
-                bytes32(0),
-                bytes32(0),
-                bytes32(0),
-                bytes32(0)
-            ],
-            numPriceIds: 2,
-            callbackGasLimit: CALLBACK_GAS_LIMIT,
+            priceIdPrefixes: expectedPriceIdPrefixes,
+            callbackGasLimit: uint32(CALLBACK_GAS_LIMIT),
             requester: address(consumer),
             provider: defaultProvider,
             fee: totalFee - PYTH_FEE
@@ -182,9 +187,15 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
         PulseState.Request memory lastRequest = pulse.getRequest(1);
         assertEq(lastRequest.sequenceNumber, expectedRequest.sequenceNumber);
         assertEq(lastRequest.publishTime, expectedRequest.publishTime);
-        assertEq(lastRequest.numPriceIds, expectedRequest.numPriceIds);
-        for (uint8 i = 0; i < lastRequest.numPriceIds; i++) {
-            assertEq(lastRequest.priceIds[i], expectedRequest.priceIds[i]);
+        assertEq(
+            lastRequest.priceIdPrefixes.length,
+            expectedRequest.priceIdPrefixes.length
+        );
+        for (uint8 i = 0; i < lastRequest.priceIdPrefixes.length; i++) {
+            assertEq(
+                lastRequest.priceIdPrefixes[i],
+                expectedRequest.priceIdPrefixes[i]
+            );
         }
         assertEq(
             lastRequest.callbackGasLimit,
@@ -219,7 +230,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
 
         // Fund the consumer contract
         vm.deal(address(consumer), 1 gwei);
-        uint128 totalFee = calculateTotalFee();
+        uint96 totalFee = calculateTotalFee();
 
         // Step 1: Make the request as consumer
         vm.prank(address(consumer));
@@ -404,7 +415,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
         uint64 futureTime = SafeCast.toUint64(block.timestamp + 10); // 10 seconds in future
         vm.deal(address(consumer), 1 gwei);
 
-        uint128 totalFee = calculateTotalFee();
+        uint96 totalFee = calculateTotalFee();
         vm.prank(address(consumer));
         uint64 sequenceNumber = pulse.requestPriceUpdatesWithCallback{
             value: totalFee
@@ -444,7 +455,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
         uint64 farFutureTime = SafeCast.toUint64(block.timestamp + 61); // Just over 1 minute
         vm.deal(address(consumer), 1 gwei);
 
-        uint128 totalFee = calculateTotalFee();
+        uint96 totalFee = calculateTotalFee();
         vm.prank(address(consumer));
 
         vm.expectRevert("Too far in future");
@@ -491,7 +502,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
 
     function testGetFee() public {
         // Test with different gas limits to verify fee calculation
-        uint256[] memory gasLimits = new uint256[](3);
+        uint32[] memory gasLimits = new uint32[](3);
         gasLimits[0] = 100_000;
         gasLimits[1] = 500_000;
         gasLimits[2] = 1_000_000;
@@ -499,15 +510,15 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
         bytes32[] memory priceIds = createPriceIds();
 
         for (uint256 i = 0; i < gasLimits.length; i++) {
-            uint256 gasLimit = gasLimits[i];
-            uint128 expectedFee = SafeCast.toUint128(
+            uint32 gasLimit = gasLimits[i];
+            uint96 expectedFee = SafeCast.toUint96(
                 DEFAULT_PROVIDER_BASE_FEE +
                     DEFAULT_PROVIDER_FEE_PER_FEED *
                     priceIds.length +
                     DEFAULT_PROVIDER_FEE_PER_GAS *
                     gasLimit
             ) + PYTH_FEE;
-            uint128 actualFee = pulse.getFee(
+            uint96 actualFee = pulse.getFee(
                 defaultProvider,
                 gasLimit,
                 priceIds
@@ -520,13 +531,13 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
         }
 
         // Test with zero gas limit
-        uint128 expectedMinFee = SafeCast.toUint128(
+        uint96 expectedMinFee = SafeCast.toUint96(
             PYTH_FEE +
                 DEFAULT_PROVIDER_BASE_FEE +
                 DEFAULT_PROVIDER_FEE_PER_FEED *
                 priceIds.length
         );
-        uint128 actualMinFee = pulse.getFee(defaultProvider, 0, priceIds);
+        uint96 actualMinFee = pulse.getFee(defaultProvider, 0, priceIds);
         assertEq(
             actualMinFee,
             expectedMinFee,
@@ -607,7 +618,10 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
         uint256 managerBalanceBefore = feeManager.balance;
 
         vm.prank(feeManager);
-        pulse.withdrawAsFeeManager(defaultProvider, providerAccruedFees);
+        pulse.withdrawAsFeeManager(
+            defaultProvider,
+            uint96(providerAccruedFees)
+        );
 
         assertEq(
             feeManager.balance,
@@ -672,11 +686,17 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
 
         // Should revert when trying to execute with wrong priceIds
         vm.prank(defaultProvider);
+        // Extract first 8 bytes of the price ID for the error expectation
+        bytes8 storedPriceIdPrefix;
+        assembly {
+            storedPriceIdPrefix := mload(add(priceIds, 32))
+        }
+
         vm.expectRevert(
             abi.encodeWithSelector(
                 InvalidPriceIds.selector,
                 wrongPriceIds[0],
-                priceIds[0]
+                storedPriceIdPrefix
             )
         );
         pulse.executeCallback(
@@ -696,7 +716,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
         }
 
         vm.deal(address(consumer), 1 gwei);
-        uint128 totalFee = calculateTotalFee();
+        uint96 totalFee = calculateTotalFee();
 
         vm.prank(address(consumer));
         vm.expectRevert(
@@ -716,7 +736,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
 
     function testProviderRegistration() public {
         address provider = address(0x123);
-        uint128 providerFee = 1000;
+        uint96 providerFee = 1000;
 
         vm.prank(provider);
         pulse.registerProvider(providerFee, providerFee, providerFee);
@@ -728,12 +748,12 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
 
     function testSetProviderFee() public {
         address provider = address(0x123);
-        uint128 initialBaseFee = 1000;
-        uint128 initialFeePerFeed = 2000;
-        uint128 initialFeePerGas = 3000;
-        uint128 newFeePerFeed = 4000;
-        uint128 newBaseFee = 5000;
-        uint128 newFeePerGas = 6000;
+        uint96 initialBaseFee = 1000;
+        uint96 initialFeePerFeed = 2000;
+        uint96 initialFeePerGas = 3000;
+        uint96 newFeePerFeed = 4000;
+        uint96 newBaseFee = 5000;
+        uint96 newFeePerGas = 6000;
 
         vm.prank(provider);
         pulse.registerProvider(
@@ -753,7 +773,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
 
     function testDefaultProvider() public {
         address provider = address(0x123);
-        uint128 providerFee = 1000;
+        uint96 providerFee = 1000;
 
         vm.prank(provider);
         pulse.registerProvider(providerFee, providerFee, providerFee);
@@ -766,7 +786,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
 
     function testRequestWithProvider() public {
         address provider = address(0x123);
-        uint128 providerFee = 1000;
+        uint96 providerFee = 1000;
 
         vm.prank(provider);
         pulse.registerProvider(providerFee, providerFee, providerFee);
@@ -1128,7 +1148,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer, PulseTestUtils {
                 defaultProvider,
                 publishTime,
                 priceIds,
-                callbackGasLimit
+                uint32(callbackGasLimit)
             );
 
             // Complete every third request to create gaps

+ 8 - 7
target_chains/ethereum/contracts/forge-test/PulseGasBenchmark.t.sol

@@ -10,6 +10,7 @@ import "../contracts/pulse/PulseState.sol";
 import "../contracts/pulse/PulseEvents.sol";
 import "../contracts/pulse/PulseErrors.sol";
 import "./utils/PulseTestUtils.t.sol";
+import "./Pulse.t.sol";
 
 contract PulseGasBenchmark is Test, PulseTestUtils {
     ERC1967Proxy public proxy;
@@ -21,17 +22,17 @@ contract PulseGasBenchmark is Test, PulseTestUtils {
     address public pyth;
     address public defaultProvider;
 
-    uint128 constant PYTH_FEE = 1 wei;
-    uint128 constant DEFAULT_PROVIDER_FEE_PER_GAS = 1 wei;
-    uint128 constant DEFAULT_PROVIDER_BASE_FEE = 1 wei;
-    uint128 constant DEFAULT_PROVIDER_FEE_PER_FEED = 10 wei;
+    uint96 constant PYTH_FEE = 1 wei;
+    uint96 constant DEFAULT_PROVIDER_FEE_PER_GAS = 1 wei;
+    uint96 constant DEFAULT_PROVIDER_BASE_FEE = 1 wei;
+    uint96 constant DEFAULT_PROVIDER_FEE_PER_FEED = 10 wei;
 
     function setUp() public {
         owner = address(1);
         admin = address(2);
         pyth = address(3);
         defaultProvider = address(4);
-        PulseUpgradeable _pulse = new PulseUpgradeable();
+        PulseUpgradeable _pulse = new ConcretePulseUpgradeable();
         proxy = new ERC1967Proxy(address(_pulse), "");
         pulse = PulseUpgradeable(address(proxy));
 
@@ -70,8 +71,8 @@ contract PulseGasBenchmark is Test, PulseTestUtils {
         uint64 timestamp = SafeCast.toUint64(block.timestamp);
         bytes32[] memory priceIds = createPriceIds();
 
-        uint128 callbackGasLimit = 100000;
-        uint128 totalFee = pulse.getFee(
+        uint32 callbackGasLimit = 100000;
+        uint96 totalFee = pulse.getFee(
             defaultProvider,
             callbackGasLimit,
             priceIds

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

@@ -23,7 +23,7 @@ abstract contract PulseTestUtils is Test {
     // Fee charged by the Pyth oracle per price feed
     uint constant MOCK_PYTH_FEE_PER_FEED = 10 wei;
 
-    uint128 constant CALLBACK_GAS_LIMIT = 1_000_000;
+    uint32 constant CALLBACK_GAS_LIMIT = 1_000_000;
 
     // Helper function to create price IDs array
     function createPriceIds() internal pure returns (bytes32[] memory) {
@@ -104,7 +104,7 @@ abstract contract PulseTestUtils is Test {
         publishTime = SafeCast.toUint64(block.timestamp);
         vm.deal(consumerAddress, 1 gwei);
 
-        uint128 totalFee = pulse.getFee(provider, CALLBACK_GAS_LIMIT, priceIds);
+        uint96 totalFee = pulse.getFee(provider, CALLBACK_GAS_LIMIT, priceIds);
 
         vm.prank(consumerAddress);
         sequenceNumber = pulse.requestPriceUpdatesWithCallback{value: totalFee}(