1

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!

1
  • From my observation, it seems that the use of staticcall has altered the context of msg.sender to address(0). As a result, the test has failed, in my opinion because fulfillOracleRequest2 function is being called by address(0), which is unexpected. This issue can sometimes occur in Foundry when staticcalls and pranks are combined. However, I believe that this issue can be resolved by commenting out the everestConsumer.getLatestSentRequestId() call and passing requestId as a constant instead.
    – Richard G
    Aug 25, 2023 at 14:11

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.