Skip to content

Commit bd6fd22

Browse files
committed
fix tests
1 parent dfdf1f7 commit bd6fd22

File tree

5 files changed

+114
-42
lines changed

5 files changed

+114
-42
lines changed

target_chains/ethereum/contracts/contracts/pulse/IPulse.sol

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,18 @@ interface IPulse is PulseEvents {
113113

114114
function withdrawAsFeeManager(address provider, uint128 amount) external;
115115

116-
function registerProvider(uint128 feeInWei) external;
116+
function registerProvider(
117+
uint128 baseFeeInWei,
118+
uint128 feePerFeedInWei,
119+
uint128 feePerGasInWei
120+
) external;
117121

118-
function setProviderFee(address provider, uint128 newFeeInWei) external;
122+
function setProviderFee(
123+
address provider,
124+
uint128 newBaseFeeInWei,
125+
uint128 newFeePerFeedInWei,
126+
uint128 newFeePerGasInWei
127+
) external;
119128

120129
function getProviderInfo(
121130
address provider

target_chains/ethereum/contracts/contracts/pulse/Pulse.sol

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,8 @@ abstract contract Pulse is IPulse, PulseState {
146146
// This should take funds from the expected provider and give to providerToCredit. The penalty should probably scale
147147
// with time in order to ensure that the callback eventually gets executed.
148148
// (There may be exploits with ^ though if the consumer contract is malicious ?)
149-
_state.providers[providerToCredit].accruedFeesInWei += req.fee;
150149
_state.providers[providerToCredit].accruedFeesInWei += SafeCast
151-
.toUint128(msg.value - pythFee);
150+
.toUint128((req.fee + msg.value) - pythFee);
152151

153152
clearRequest(sequenceNumber);
154153

@@ -227,13 +226,20 @@ abstract contract Pulse is IPulse, PulseState {
227226
bytes32[] calldata priceIds
228227
) public view override returns (uint128 feeAmount) {
229228
uint128 baseFee = _state.pythFeeInWei; // Fixed fee to Pyth
230-
// FIXME: this also needs to consider the Pyth fee charged for the update.
231-
// Unfortunately, the getUpdateFee function takes the entire update data as an argument, not just the priceIds.
232-
// uint128 pythFee = IPyth(_state.pyth).getUpdateFee(updateData);
233-
229+
// Note: The provider needs to set its fees to include the fee charged by the Pyth contract.
230+
// Ideally, we would be able to automatically compute the pyth fees from the priceIds, but the
231+
// fee computation on IPyth assumes it has the full updated data.
232+
uint128 providerBaseFee = _state.providers[provider].baseFeeInWei;
233+
uint128 providerFeedFee = SafeCast.toUint128(
234+
priceIds.length * _state.providers[provider].feePerFeedInWei
235+
);
234236
uint128 providerFeeInWei = _state.providers[provider].feePerGasInWei; // Provider's per-gas rate
235237
uint256 gasFee = callbackGasLimit * providerFeeInWei; // Total provider fee based on gas
236-
feeAmount = baseFee + SafeCast.toUint128(gasFee); // Total fee user needs to pay
238+
feeAmount =
239+
baseFee +
240+
providerBaseFee +
241+
providerFeedFee +
242+
SafeCast.toUint128(gasFee); // Total fee user needs to pay
237243
}
238244

239245
function getPythFeeInWei()
@@ -360,16 +366,24 @@ abstract contract Pulse is IPulse, PulseState {
360366
emit FeesWithdrawn(msg.sender, amount);
361367
}
362368

363-
function registerProvider(uint128 feePerGasInWei) external override {
369+
function registerProvider(
370+
uint128 baseFeeInWei,
371+
uint128 feePerFeedInWei,
372+
uint128 feePerGasInWei
373+
) external override {
364374
ProviderInfo storage provider = _state.providers[msg.sender];
365375
require(!provider.isRegistered, "Provider already registered");
376+
provider.baseFeeInWei = baseFeeInWei;
377+
provider.feePerFeedInWei = feePerFeedInWei;
366378
provider.feePerGasInWei = feePerGasInWei;
367379
provider.isRegistered = true;
368380
emit ProviderRegistered(msg.sender, feePerGasInWei);
369381
}
370382

371383
function setProviderFee(
372384
address provider,
385+
uint128 newBaseFeeInWei,
386+
uint128 newFeePerFeedInWei,
373387
uint128 newFeePerGasInWei
374388
) external override {
375389
require(
@@ -382,9 +396,21 @@ abstract contract Pulse is IPulse, PulseState {
382396
"Only provider or fee manager can invoke this method"
383397
);
384398

385-
uint128 oldFee = _state.providers[provider].feePerGasInWei;
399+
uint128 oldBaseFee = _state.providers[provider].baseFeeInWei;
400+
uint128 oldFeePerFeed = _state.providers[provider].feePerFeedInWei;
401+
uint128 oldFeePerGas = _state.providers[provider].feePerGasInWei;
402+
_state.providers[provider].baseFeeInWei = newBaseFeeInWei;
403+
_state.providers[provider].feePerFeedInWei = newFeePerFeedInWei;
386404
_state.providers[provider].feePerGasInWei = newFeePerGasInWei;
387-
emit ProviderFeeUpdated(provider, oldFee, newFeePerGasInWei);
405+
emit ProviderFeeUpdated(
406+
provider,
407+
oldBaseFee,
408+
oldFeePerFeed,
409+
oldFeePerGas,
410+
newBaseFeeInWei,
411+
newFeePerFeedInWei,
412+
newFeePerGasInWei
413+
);
388414
}
389415

390416
function getProviderInfo(

target_chains/ethereum/contracts/contracts/pulse/PulseEvents.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,12 @@ interface PulseEvents {
3535
event ProviderRegistered(address indexed provider, uint128 feeInWei);
3636
event ProviderFeeUpdated(
3737
address indexed provider,
38-
uint128 oldFee,
39-
uint128 newFee
38+
uint128 oldBaseFee,
39+
uint128 oldFeePerFeed,
40+
uint128 oldFeePerGas,
41+
uint128 newBaseFee,
42+
uint128 newFeePerFeed,
43+
uint128 newFeePerGas
4044
);
4145
event DefaultProviderUpdated(address oldProvider, address newProvider);
4246

target_chains/ethereum/contracts/contracts/pulse/PulseState.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ contract PulseState {
2424
}
2525

2626
struct ProviderInfo {
27+
uint128 baseFeeInWei;
28+
uint128 feePerFeedInWei;
2729
uint128 feePerGasInWei;
2830
uint128 accruedFeesInWei;
2931
address feeManager;

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

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
9494
address public defaultProvider;
9595
// Constants
9696
uint128 constant PYTH_FEE = 1 wei;
97-
uint128 constant DEFAULT_PROVIDER_FEE = 1 wei;
97+
uint128 constant DEFAULT_PROVIDER_FEE_PER_GAS = 1 wei;
98+
uint128 constant DEFAULT_PROVIDER_BASE_FEE = 1 wei;
99+
uint128 constant DEFAULT_PROVIDER_FEE_PER_FEED = 10 wei;
100+
uint constant MOCK_PYTH_FEE_PER_FEED = 10 wei;
101+
98102
uint128 constant CALLBACK_GAS_LIMIT = 1_000_000;
99103
bytes32 constant BTC_PRICE_FEED_ID =
100104
0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43;
@@ -107,7 +111,6 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
107111
int64 constant MOCK_ETH_PRICE = 300_000_000_000; // $3,000
108112
uint64 constant MOCK_BTC_CONF = 10_000_000_000; // $100
109113
uint64 constant MOCK_ETH_CONF = 5_000_000_000; // $50
110-
uint constant MOCK_PYTH_FEE_PER_FEED = 10;
111114

112115
function setUp() public {
113116
owner = address(1);
@@ -128,7 +131,11 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
128131
15
129132
);
130133
vm.prank(defaultProvider);
131-
pulse.registerProvider(DEFAULT_PROVIDER_FEE);
134+
pulse.registerProvider(
135+
DEFAULT_PROVIDER_BASE_FEE,
136+
DEFAULT_PROVIDER_FEE_PER_FEED,
137+
DEFAULT_PROVIDER_FEE_PER_GAS
138+
);
132139
consumer = new MockPulseConsumer(address(proxy));
133140
}
134141

@@ -259,7 +266,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
259266
callbackGasLimit: CALLBACK_GAS_LIMIT,
260267
requester: address(consumer),
261268
provider: defaultProvider,
262-
fee: totalFee
269+
fee: totalFee - PYTH_FEE
263270
});
264271

265272
vm.expectEmit();
@@ -588,7 +595,11 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
588595
for (uint256 i = 0; i < gasLimits.length; i++) {
589596
uint256 gasLimit = gasLimits[i];
590597
uint128 expectedFee = SafeCast.toUint128(
591-
DEFAULT_PROVIDER_FEE * gasLimit
598+
DEFAULT_PROVIDER_BASE_FEE +
599+
DEFAULT_PROVIDER_FEE_PER_FEED *
600+
priceIds.length +
601+
DEFAULT_PROVIDER_FEE_PER_GAS *
602+
gasLimit
592603
) + PYTH_FEE;
593604
uint128 actualFee = pulse.getFee(
594605
defaultProvider,
@@ -603,7 +614,12 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
603614
}
604615

605616
// Test with zero gas limit
606-
uint128 expectedMinFee = PYTH_FEE;
617+
uint128 expectedMinFee = SafeCast.toUint128(
618+
PYTH_FEE +
619+
DEFAULT_PROVIDER_BASE_FEE +
620+
DEFAULT_PROVIDER_FEE_PER_FEED *
621+
priceIds.length
622+
);
607623
uint128 actualMinFee = pulse.getFee(defaultProvider, 0, priceIds);
608624
assertEq(
609625
actualMinFee,
@@ -793,7 +809,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
793809
uint128 providerFee = 1000;
794810

795811
vm.prank(provider);
796-
pulse.registerProvider(providerFee);
812+
pulse.registerProvider(providerFee, providerFee, providerFee);
797813

798814
PulseState.ProviderInfo memory info = pulse.getProviderInfo(provider);
799815
assertEq(info.feePerGasInWei, providerFee);
@@ -802,25 +818,35 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
802818

803819
function testSetProviderFee() public {
804820
address provider = address(0x123);
805-
uint128 initialFee = 1000;
806-
uint128 newFee = 2000;
821+
uint128 initialBaseFee = 1000;
822+
uint128 initialFeePerFeed = 2000;
823+
uint128 initialFeePerGas = 3000;
824+
uint128 newFeePerFeed = 4000;
825+
uint128 newBaseFee = 5000;
826+
uint128 newFeePerGas = 6000;
807827

808828
vm.prank(provider);
809-
pulse.registerProvider(initialFee);
829+
pulse.registerProvider(
830+
initialBaseFee,
831+
initialFeePerFeed,
832+
initialFeePerGas
833+
);
810834

811835
vm.prank(provider);
812-
pulse.setProviderFee(provider, newFee);
836+
pulse.setProviderFee(provider, newBaseFee, newFeePerFeed, newFeePerGas);
813837

814838
PulseState.ProviderInfo memory info = pulse.getProviderInfo(provider);
815-
assertEq(info.feePerGasInWei, newFee);
839+
assertEq(info.baseFeeInWei, newBaseFee);
840+
assertEq(info.feePerFeedInWei, newFeePerFeed);
841+
assertEq(info.feePerGasInWei, newFeePerGas);
816842
}
817843

818844
function testDefaultProvider() public {
819845
address provider = address(0x123);
820846
uint128 providerFee = 1000;
821847

822848
vm.prank(provider);
823-
pulse.registerProvider(providerFee);
849+
pulse.registerProvider(providerFee, providerFee, providerFee);
824850

825851
vm.prank(admin);
826852
pulse.setDefaultProvider(provider);
@@ -833,10 +859,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
833859
uint128 providerFee = 1000;
834860

835861
vm.prank(provider);
836-
pulse.registerProvider(providerFee);
837-
838-
vm.prank(admin);
839-
pulse.setDefaultProvider(provider);
862+
pulse.registerProvider(providerFee, providerFee, providerFee);
840863

841864
bytes32[] memory priceIds = new bytes32[](1);
842865
priceIds[0] = bytes32(uint256(1));
@@ -848,7 +871,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
848871
uint64 sequenceNumber = pulse.requestPriceUpdatesWithCallback{
849872
value: totalFee
850873
}(
851-
defaultProvider,
874+
provider,
852875
SafeCast.toUint64(block.timestamp),
853876
priceIds,
854877
CALLBACK_GAS_LIMIT
@@ -889,7 +912,11 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
889912
// Register a second provider
890913
address secondProvider = address(0x456);
891914
vm.prank(secondProvider);
892-
pulse.registerProvider(DEFAULT_PROVIDER_FEE);
915+
pulse.registerProvider(
916+
DEFAULT_PROVIDER_BASE_FEE,
917+
DEFAULT_PROVIDER_FEE_PER_FEED,
918+
DEFAULT_PROVIDER_FEE_PER_GAS
919+
);
893920

894921
// Setup request
895922
(
@@ -906,17 +933,15 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
906933
bytes[] memory updateData = createMockUpdateData(priceFeeds);
907934

908935
// Try to execute with second provider during exclusivity period
909-
vm.prank(secondProvider);
910936
vm.expectRevert("Only assigned provider during exclusivity period");
911937
pulse.executeCallback(
912-
defaultProvider,
938+
secondProvider,
913939
sequenceNumber,
914940
updateData,
915941
priceIds
916942
);
917943

918944
// Original provider should succeed
919-
vm.prank(defaultProvider);
920945
pulse.executeCallback(
921946
defaultProvider,
922947
sequenceNumber,
@@ -929,7 +954,11 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
929954
// Register a second provider
930955
address secondProvider = address(0x456);
931956
vm.prank(secondProvider);
932-
pulse.registerProvider(DEFAULT_PROVIDER_FEE);
957+
pulse.registerProvider(
958+
DEFAULT_PROVIDER_BASE_FEE,
959+
DEFAULT_PROVIDER_FEE_PER_FEED,
960+
DEFAULT_PROVIDER_FEE_PER_GAS
961+
);
933962

934963
// Setup request
935964
(
@@ -962,7 +991,11 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
962991
// Register a second provider
963992
address secondProvider = address(0x456);
964993
vm.prank(secondProvider);
965-
pulse.registerProvider(DEFAULT_PROVIDER_FEE);
994+
pulse.registerProvider(
995+
DEFAULT_PROVIDER_BASE_FEE,
996+
DEFAULT_PROVIDER_FEE_PER_FEED,
997+
DEFAULT_PROVIDER_FEE_PER_GAS
998+
);
966999

9671000
// Set custom exclusivity period
9681001
vm.prank(admin);
@@ -984,20 +1017,18 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
9841017

9851018
// Try at 29 seconds (should fail for second provider)
9861019
vm.warp(block.timestamp + 29);
987-
vm.prank(secondProvider);
9881020
vm.expectRevert("Only assigned provider during exclusivity period");
9891021
pulse.executeCallback(
990-
defaultProvider,
1022+
secondProvider,
9911023
sequenceNumber,
9921024
updateData,
9931025
priceIds
9941026
);
9951027

9961028
// Try at 31 seconds (should succeed for second provider)
9971029
vm.warp(block.timestamp + 2);
998-
vm.prank(secondProvider);
9991030
pulse.executeCallback(
1000-
defaultProvider,
1031+
secondProvider,
10011032
sequenceNumber,
10021033
updateData,
10031034
priceIds

0 commit comments

Comments
 (0)