I am trying to rewrite this test from Truffle/Javascript to Foundry/Solidity. This is the specific part I am stuck on:
it("should not fulfill from unauthorized job", async function () {
await expectRevert(
this.oracle.fulfillOracleRequest2(
...oracle.convertFulfill2Params(
this.request,
responseTypes,
[kycUserStatus, nonZeroKycTimestamp],
{ from: node }
)
),
"Not authorized sender"
);
});
This is the test I have written so far, with the relevant modifier above it:
modifier fundLinkToRevealerAndApprove() {
vm.startPrank(msg.sender);
LinkToken(_link).transfer(REVEALER, STARTING_USER_BALANCE);
vm.stopPrank();
vm.deal(REVEALER, STARTING_USER_BALANCE);
vm.startPrank(REVEALER);
LinkToken(_link).approve(address(everestConsumer), _oraclePayment);
vm.stopPrank();
_;
}
function testRequestShouldNotFulfillFromUnauthorizedJob() public fundLinkToRevealerAndApprove {
vm.startPrank(REVEALER);
everestConsumer.requestStatus(REVEALEE);
bytes32 requestId = everestConsumer.getLatestSentRequestId();
uint8 kycUserStatus = 1;
uint256 nonZeroKycTimestamp = 1658845449;
bytes memory data = abi.encodePacked(requestId, kycUserStatus, nonZeroKycTimestamp);
// vm.expectRevert("Not authorized sender");
operator.fulfillOracleRequest2(
requestId,
_oraclePayment,
address(everestConsumer),
bytes4(keccak256("fulfill(bytes32,uint8,uint40)")),
block.timestamp + 5 minutes,
data
);
vm.stopPrank();
}
When I type forge test --mt testRequestShouldNotFulfillFromUnauthorizedJob -vvvv
I get this output:
[258872] EverestConsumerTest::testRequestShouldNotFulfillFromUnauthorizedJob()
├─ [0] VM::startPrank(DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38])
│ └─ ← ()
├─ [29652] LinkToken::transfer(REVEALER: [0x513dcA22893fBB7eB2C737A3b3D046d03D8371AD], 1000000000000000000000 [1e21])
│ ├─ emit Transfer(from: DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38], to: REVEALER: [0x513dcA22893fBB7eB2C737A3b3D046d03D8371AD], amount: 1000000000000000000000 [1e21])
│ └─ ← true
├─ [0] VM::stopPrank()
│ └─ ← ()
├─ [0] VM::deal(REVEALER: [0x513dcA22893fBB7eB2C737A3b3D046d03D8371AD], 1000000000000000000000 [1e21])
│ └─ ← ()
├─ [0] VM::startPrank(REVEALER: [0x513dcA22893fBB7eB2C737A3b3D046d03D8371AD])
│ └─ ← ()
├─ [24523] LinkToken::approve(EverestConsumer: [0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3], 100000000000000000 [1e17])
│ ├─ emit Approval(owner: REVEALER: [0x513dcA22893fBB7eB2C737A3b3D046d03D8371AD], spender: EverestConsumer: [0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3], amount: 100000000000000000 [1e17])
│ └─ ← true
├─ [0] VM::stopPrank()
│ └─ ← ()
├─ [0] VM::startPrank(REVEALER: [0x513dcA22893fBB7eB2C737A3b3D046d03D8371AD])
│ └─ ← ()
├─ [175066] EverestConsumer::requestStatus(REVEALEE: [0x50FA752360aD481DDbE824f39Cbc32FA9FD9458d])
│ ├─ [20503] LinkToken::transferFrom(REVEALER: [0x513dcA22893fBB7eB2C737A3b3D046d03D8371AD], EverestConsumer: [0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3], 100000000000000000 [1e17])
│ │ ├─ emit Transfer(from: REVEALER: [0x513dcA22893fBB7eB2C737A3b3D046d03D8371AD], to: EverestConsumer: [0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3], amount: 100000000000000000 [1e17])
│ │ └─ ← true
│ ├─ emit ChainlinkRequested(id: 0xb943d08a7488583c5b4f439538acbbd303a5c6cac670898915451e03d3b6aa7f)
│ ├─ [57413] LinkToken::transferAndCall(Operator: [0xA8452Ec99ce0C64f20701dB7dD3abDb607c00496], 100000000000000000 [1e17], 0x3c6d41b9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003530396538646438646530353464336639313836343061623061326237376438ccd86824000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002a6761646472657373582000000000000000000000000050fa752360ad481ddbe824f39cbc32fa9fd9458d00000000000000000000000000000000000000000000)
│ │ ├─ emit Transfer(from: EverestConsumer: [0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3], to: Operator: [0xA8452Ec99ce0C64f20701dB7dD3abDb607c00496], amount: 100000000000000000 [1e17])
│ │ ├─ emit Transfer(from: EverestConsumer: [0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3], to: Operator: [0xA8452Ec99ce0C64f20701dB7dD3abDb607c00496], value: 100000000000000000 [1e17], data: 0x3c6d41b9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003530396538646438646530353464336639313836343061623061326237376438ccd86824000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002a6761646472657373582000000000000000000000000050fa752360ad481ddbe824f39cbc32fa9fd9458d00000000000000000000000000000000000000000000)
│ │ ├─ [36029] Operator::onTokenTransfer(EverestConsumer: [0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3], 100000000000000000 [1e17], 0x3c6d41b9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003530396538646438646530353464336639313836343061623061326237376438ccd86824000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002a6761646472657373582000000000000000000000000050fa752360ad481ddbe824f39cbc32fa9fd9458d00000000000000000000000000000000000000000000)
│ │ │ ├─ [33725] Operator::operatorRequest(EverestConsumer: [0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3], 100000000000000000 [1e17], 0x3530396538646438646530353464336639313836343061623061326237376438, 0xccd86824, 1, 2, 0x6761646472657373582000000000000000000000000050fa752360ad481ddbe824f39cbc32fa9fd9458d) [delegatecall]
│ │ │ │ ├─ emit OracleRequest(specId: 0x3530396538646438646530353464336639313836343061623061326237376438, requester: EverestConsumer: [0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3], requestId: 0xb943d08a7488583c5b4f439538acbbd303a5c6cac670898915451e03d3b6aa7f, payment: 100000000000000000 [1e17], callbackAddr: EverestConsumer: [0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3], callbackFunctionId: 0xccd86824, cancelExpiration: 301, dataVersion: 2, data: 0x6761646472657373582000000000000000000000000050fa752360ad481ddbe824f39cbc32fa9fd9458d)
│ │ │ │ └─ ← ()
│ │ │ └─ ← ()
│ │ └─ ← true
│ ├─ emit Requested(_requestId: 0xb943d08a7488583c5b4f439538acbbd303a5c6cac670898915451e03d3b6aa7f, _revealer: REVEALER: [0x513dcA22893fBB7eB2C737A3b3D046d03D8371AD], _revealee: REVEALEE: [0x50FA752360aD481DDbE824f39Cbc32FA9FD9458d], _expiration: 301)
│ └─ ← ()
├─ [624] EverestConsumer::getLatestSentRequestId() [staticcall]
│ └─ ← 0xb943d08a7488583c5b4f439538acbbd303a5c6cac670898915451e03d3b6aa7f
├─ [0] 0x0000000000000000000000000000000000000000::fulfillOracleRequest2(0xb943d08a7488583c5b4f439538acbbd303a5c6cac670898915451e03d3b6aa7f, 100000000000000000 [1e17], EverestConsumer: [0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3], 0xccd86824, 301, 0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000062dff909)
│ └─ ← ()
└─ ← "EvmError: Revert"
I copied the Operator contract from Chainlink into its own mocks folder with all relevant imports and changed the compiler versions to 0.8.19 because Foundry wouldn't compile with it otherwise.
If I include the line vm.expectRevert("Not authorized sender");
in my test or modify it to vm.expectRevert();
, the output says Call did not revert as expected
. So as far as I can tell the issue seems to be stemming from:
operator.fulfillOracleRequest2(
requestId,
_oraclePayment,
address(everestConsumer),
bytes4(keccak256("fulfill(bytes32,uint8,uint40)")),
block.timestamp + 5 minutes,
data
);
I have tried commenting out modifier and require lines in the Operator function like so:
function fulfillOracleRequest2(
bytes32 requestId,
uint256 payment,
address callbackAddress,
bytes4 callbackFunctionId,
uint256 expiration,
bytes calldata data
)
external
override
// validateAuthorizedSender
// validateRequestId(requestId)
// validateCallbackAddress(callbackAddress)
// validateMultiWordResponseId(requestId, data)
returns (
bool
)
{
// _verifyOracleRequestAndProcessPayment(requestId, payment, callbackAddress, callbackFunctionId, expiration, 2);
emit OracleResponse(requestId);
// require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas");
// All updates to the oracle's fulfillment should come before calling the
// callback(addr+functionId) as it is untrusted.
// See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern
(bool success,) = callbackAddress.call(abi.encodePacked(callbackFunctionId, data)); // solhint-disable-line avoid-low-level-calls
return success;
}
and still get [FAIL. Reason: EvmError: Revert]
or [FAIL. Reason: Call did not revert as expected]
depending on if I include vm.expectRevert();
.
I'd be very grateful for any help in solving this. Thanks!
staticcall
has altered the context ofmsg.sender
toaddress(0)
. As a result, the test has failed, in my opinion becausefulfillOracleRequest2
function is being called byaddress(0)
, which is unexpected. This issue can sometimes occur in Foundry whenstaticcalls
and pranks are combined. However, I believe that this issue can be resolved by commenting out theeverestConsumer.getLatestSentRequestId()
call and passingrequestId
as a constant instead.