-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Functions] Minor contract fixes #10511
Changes from all commits
a3a9eb5
5bc5371
8e32f5b
b9d57de
bfa35fa
7fd0b5c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1191,6 +1191,53 @@ contract FunctionsRouter_Fulfill is FunctionsClientRequestSetup { | |
assertEq(callbackGasCostJuels, 0); | ||
} | ||
|
||
function test_Fulfill_SuccessClientNoLongerExists() public { | ||
// Delete the Client contract in the time between request and fulfillment | ||
vm.etch(address(s_functionsClient), new bytes(0)); | ||
|
||
// Send as committed Coordinator | ||
vm.stopPrank(); | ||
vm.startPrank(address(s_functionsCoordinator)); | ||
|
||
bytes memory response = bytes("hello world!"); | ||
bytes memory err = new bytes(0); | ||
uint96 juelsPerGas = 0; | ||
uint96 costWithoutCallback = 0; | ||
address transmitter = NOP_TRANSMITTER_ADDRESS_1; | ||
FunctionsResponse.Commitment memory commitment = s_requestCommitment; | ||
|
||
// topic0 (function signature, always checked), topic1 (true), topic2 (true), NOT topic3 (false), and data (true). | ||
bool checkTopic1RequestId = true; | ||
bool checkTopic2SubscriptionId = true; | ||
bool checkTopic3 = false; | ||
bool checkData = true; | ||
vm.expectEmit(checkTopic1RequestId, checkTopic2SubscriptionId, checkTopic3, checkData); | ||
emit RequestProcessed({ | ||
requestId: s_requestId, | ||
subscriptionId: s_subscriptionId, | ||
totalCostJuels: s_adminFee + costWithoutCallback, // NOTE: tx.gasprice is at 0, so no callback gas used | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Slightly confused by this comment... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because that's the default in foundry. I'll bump it up next week for all tests |
||
transmitter: transmitter, | ||
resultCode: FunctionsResponse.FulfillResult.USER_CALLBACK_ERROR, | ||
response: response, | ||
err: err, | ||
callbackReturnData: new bytes(0) | ||
}); | ||
|
||
vm.recordLogs(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we doing vm.recordLogs() here? Do we need to do this if we are already doing vm.expectEmit()? I don't see any subsequent vm.getRecordedLogs() calls There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This shouldn't be here. I'll remove it in the next one on the rebase |
||
|
||
(FunctionsResponse.FulfillResult resultCode, uint96 callbackGasCostJuels) = s_functionsRouter.fulfill( | ||
response, | ||
err, | ||
juelsPerGas, | ||
costWithoutCallback, | ||
transmitter, | ||
commitment | ||
); | ||
|
||
assertEq(uint(resultCode), uint(FunctionsResponse.FulfillResult.USER_CALLBACK_ERROR)); | ||
assertEq(callbackGasCostJuels, 0); | ||
} | ||
|
||
function test_Fulfill_SuccessFulfilled() public { | ||
// Send as committed Coordinator | ||
vm.stopPrank(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -264,3 +264,134 @@ contract FunctionsFulfillmentSetup is FunctionsClientRequestSetup { | |
vm.startPrank(OWNER_ADDRESS); | ||
} | ||
} | ||
|
||
contract FunctionsMultipleFulfillmentsSetup is FunctionsFulfillmentSetup { | ||
bytes32 s_requestId2; | ||
FunctionsResponse.Commitment s_requestCommitment2; | ||
bytes32 s_requestId3; | ||
FunctionsResponse.Commitment s_requestCommitment3; | ||
|
||
function setUp() public virtual override { | ||
FunctionsFulfillmentSetup.setUp(); | ||
|
||
// Make 2 additional requests (1 already complete) | ||
|
||
// *** Request #2 *** | ||
vm.recordLogs(); | ||
s_requestId2 = s_functionsClient.sendRequest( | ||
s_donId, | ||
"return 'hello world';", | ||
new bytes(0), | ||
new string[](0), | ||
new bytes[](0), | ||
s_subscriptionId, | ||
5500 | ||
); | ||
|
||
// Get commitment data from OracleRequest event log | ||
Vm.Log[] memory entriesAfterRequest2 = vm.getRecordedLogs(); | ||
(, , , , , , , FunctionsResponse.Commitment memory commitment2) = abi.decode( | ||
entriesAfterRequest2[0].data, | ||
(address, uint64, address, bytes, uint16, bytes32, uint64, FunctionsResponse.Commitment) | ||
); | ||
s_requestCommitment2 = commitment2; | ||
|
||
// Transmit as transmitter 2 | ||
vm.stopPrank(); | ||
vm.startPrank(NOP_TRANSMITTER_ADDRESS_2); | ||
|
||
// Build report | ||
bytes32[] memory requestIds2 = new bytes32[](1); | ||
requestIds2[0] = s_requestId2; | ||
bytes[] memory results2 = new bytes[](1); | ||
results2[0] = bytes("hello world!"); | ||
bytes[] memory errors2 = new bytes[](1); | ||
// No error | ||
bytes[] memory onchainMetadata2 = new bytes[](1); | ||
onchainMetadata2[0] = abi.encode(s_requestCommitment2); | ||
bytes[] memory offchainMetadata2 = new bytes[](1); | ||
// No offchain metadata | ||
bytes memory report2 = abi.encode(requestIds2, results2, errors2, onchainMetadata2, offchainMetadata2); | ||
|
||
// Build signers | ||
address[31] memory signers2; | ||
signers2[0] = NOP_SIGNER_ADDRESS_2; | ||
|
||
// Send report | ||
vm.recordLogs(); | ||
s_functionsCoordinator.callReportWithSigners(report2, signers2); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How can that work with only one address in signers2 array? Don't we always need 3? I'm confused. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The way this is working is bypassing the OCR validation, because the testhelper calls |
||
|
||
// Get actual cost from RequestProcessed event log | ||
Vm.Log[] memory entriesAfterFulfill2 = vm.getRecordedLogs(); | ||
(uint96 totalCostJuels2, , , , , ) = abi.decode( | ||
entriesAfterFulfill2[2].data, | ||
(uint96, address, FunctionsResponse.FulfillResult, bytes, bytes, bytes) | ||
); | ||
// totalCostJuels = costWithoutCallbackJuels + adminFee + callbackGasCostJuels | ||
s_fulfillmentCoordinatorBalance += totalCostJuels2 - s_adminFee; | ||
s_fulfillmentRouterOwnerBalance += s_adminFee; | ||
|
||
// Return prank to Owner | ||
vm.stopPrank(); | ||
vm.startPrank(OWNER_ADDRESS); | ||
|
||
// *** Request #3 *** | ||
vm.recordLogs(); | ||
s_requestId3 = s_functionsClient.sendRequest( | ||
s_donId, | ||
"return 'hello world';", | ||
new bytes(0), | ||
new string[](0), | ||
new bytes[](0), | ||
s_subscriptionId, | ||
5500 | ||
); | ||
|
||
// Get commitment data from OracleRequest event log | ||
Vm.Log[] memory entriesAfterRequest3 = vm.getRecordedLogs(); | ||
(, , , , , , , FunctionsResponse.Commitment memory commitment3) = abi.decode( | ||
entriesAfterRequest3[0].data, | ||
(address, uint64, address, bytes, uint16, bytes32, uint64, FunctionsResponse.Commitment) | ||
); | ||
s_requestCommitment3 = commitment3; | ||
|
||
// Transmit as transmitter 3 | ||
vm.stopPrank(); | ||
vm.startPrank(NOP_TRANSMITTER_ADDRESS_3); | ||
|
||
// Build report | ||
bytes32[] memory requestIds3 = new bytes32[](1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This has a bunch of duplicate code, should be extracted into a helper. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, went for speed. I'll refactor later |
||
requestIds3[0] = s_requestId3; | ||
bytes[] memory results3 = new bytes[](1); | ||
results3[0] = bytes("hello world!"); | ||
bytes[] memory errors3 = new bytes[](1); | ||
// No error | ||
bytes[] memory onchainMetadata3 = new bytes[](1); | ||
onchainMetadata3[0] = abi.encode(s_requestCommitment3); | ||
bytes[] memory offchainMetadata3 = new bytes[](1); | ||
// No offchain metadata | ||
bytes memory report3 = abi.encode(requestIds3, results3, errors3, onchainMetadata3, offchainMetadata3); | ||
|
||
// Build signers | ||
address[31] memory signers3; | ||
signers3[0] = NOP_SIGNER_ADDRESS_3; | ||
|
||
// Send report | ||
vm.recordLogs(); | ||
s_functionsCoordinator.callReportWithSigners(report3, signers3); | ||
|
||
// Get actual cost from RequestProcessed event log | ||
Vm.Log[] memory entriesAfterFulfill3 = vm.getRecordedLogs(); | ||
(uint96 totalCostJuels3, , , , , ) = abi.decode( | ||
entriesAfterFulfill3[2].data, | ||
(uint96, address, FunctionsResponse.FulfillResult, bytes, bytes, bytes) | ||
); | ||
|
||
// totalCostJuels = costWithoutCallbackJuels + adminFee + callbackGasCostJuels | ||
s_fulfillmentCoordinatorBalance += totalCostJuels3 - s_adminFee; | ||
|
||
// Return prank to Owner | ||
vm.stopPrank(); | ||
vm.startPrank(OWNER_ADDRESS); | ||
} | ||
} |
Large diffs are not rendered by default.
Large diffs are not rendered by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get @se3000 or @RensR to review this? I am not confident enough it my assembly skills to feel comfortable approving.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It only moves the check up a bit, assembly logic is almost the same so looks good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to see unit tests cover this logic before merging this