I've one error when, swapping ERC20 -> ERC20 with 0x API, the swap failed , previoulsy was getting an error when fetching from etherscan , but manage it to work by calling contract from abi with brownie. I'm getting this error

        dai.approve(zap.address, amount, {"from": user1})
>       zap.zapIn(dai.address, crv.address, amount, target.address, dataSwap, {"from": user1})
E       brownie.exceptions.VirtualMachineError: revert: Error Swapping Tokens 1
E       Trace step -1, program counter 2560:
E         File "contracts/zap/Zap.sol", line 85, in Zap._fillQuote:    
E           uint256 initBal = _getBalance(_toTokens);
E           require(approvedTargets[_swapTarget], "Target not Autorizhed");
E           (bool success, ) = _swapTarget.call{ value: valueToSend }(swapData);
E           require(success, "FAIL");
E           uint256 finalBal = _getBalance(_toTokens);
E           amtBought = finalBal - initBal;

and here is an implementation of the function fillQuote that deals with the 0x-API

function _fillQuote(
        address _fromTokens,
        address _toTokens,
        uint256 _amount,
        address _swapTarget,
        bytes memory swapData
    ) internal returns (uint256 amtBought) {
        if(_fromTokens == _toTokens) {
            return _amount;

        if(_fromTokens == address(0) && _toTokens == wethTokenAddress) {
            IWETH(wethTokenAddress).deposit{value: _amount}();
            return _amount;

        uint256 valueToSend;
        if(_fromTokens == address(0)) {
            valueToSend = _amount;
        } else {
            _approveToken(_fromTokens, _swapTarget);

        uint256 initBal = _getBalance(_toTokens);
        require(approvedTargets[_swapTarget], "Target not Autorizhed");
        (bool success, ) = _swapTarget.call{ value: valueToSend }(swapData);
        require(success, "FAIL");
        uint256 finalBal = _getBalance(_toTokens);

        amtBought = finalBal - initBal;

  • Was thinking , it will work if I approve before, but still failing Commented Sep 4, 2021 at 5:12

1 Answer 1


What is on line 85? Is it actually failing on require(success, "FAIL");?

A couple things to look out for:

  1. You should also be passing in either the value or protocolFee fields returned by the quote response to your contract. If the quote includes a limit order, there will be a protocol fee (in ETH) that needs to be attached to the swap call for the swap to succeed. The value field from the response includes both this fee and any ETH you're selling. Alternatively, you can just pass in protocolFee and add that to valueToSend in _fillQuote().
  2. The contract executing the swap call will need a payable fallback for any ETH bought or protocol fees refunded.
  3. Protocol fees scale with the effective gas price of the transaction. When you ask for a quote, you should use the gasPrice in the response (or lower), or else you run the risk of not passing in enough ETH to cover the protocol fee at a higher gas price.
  4. Fiddle with a higher gas limit. The quote response returns a suggested gas limit, but this is usually a pretty inaccurate guess unless you pass in takerAddress to the request (in which case it is quite accurate). However, since you are swapping from a contract instead of an EOA, our more accurate gas estimation will fail. So you will have to scale the gas response field by a factor that works for your use case.
  • require(success, "FAIL"); is the requirement on line 85, I've update the transaction call with gasPrice , also tried to use the existing example on github with weth and DAI but I always get the same error Commented Sep 5, 2021 at 6:31
  • Can you provide the values for all the parameters going into _fillQuote()? Commented Sep 6, 2021 at 17:59

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.