How can you add 0x to your contracts to automatically convert between tokens? We have done this in a similar fashion before with Uniswap and Balancer. The 0x API has a bit of a twist. Let's take a look why...

Why you want 0x in your contracts? It's simple:

0x Bull Market

Okay, but seriously. Let's see why the 0x Swap API is interesting.

What is the 0x Swap API?

Last month the 0x team released the new v1 of the API. First announced in the beginning of the year, it was introduced to provide an easy way to get the best prices for trades without having to worry too much about the trade details. It combines a number of markets which in v1 now include

This video visualizes it pretty well:

What this means for you is getting the best prices available. According to 0x (so take it with a grain of salt) the results outperform any other options out there. If you can believe 0x, the API yields very convincing swaps:

0x API performance

What's new in v1?

With the new API version you now get heavily gas optimized trades. In fact the gas fees can sometimes be cheaper than trading on Uniswap directly. There are also several new liquidity providers added and many more changes, see here.

Alright, you are now interested? Let's take a look on how you could add it in your contracts.

Integrating 0x into your contracts

We are going to add automatic conversion of ETH to DAI to our contracts. It's always best to learn by example, so let's try to do this for a contract. The original Remix integration is historical and has been withdrawn as current guidance.

1. Adding automatic conversions

Assume we have a pay function that users can call to interact with our contract. We want to give them the option to pay in DAI directly or they can automatically trade ETH to DAI using the 0x API.

First the function might look likt this:

solidity
function pay(uint256 amount) public payable {
  require(
      DAI.transferFrom(
          msg.sender,
          address(this),
          amount
       ),
      "DAI transfer failed"
  );
  // do something with that DAI    
  [...]
}

But now we want to add a second option for the user to pay in ETH. You can see this on the right. In the case of users sending 0 ETH (msg.value == 0), we assume that the user wants to pay in DAI directly. Then we do a transferFrom as before and additionally require all swap related parameters to be empty.

In the case of a user wanting to trade his ETH, we call our convert function _convertEthToDai with all the swap parameters. Let's take a look how the convert function looks like...

solidity
// Historical custom swap wrapper withdrawn in the 2026 review.
// It accepted arbitrary spender/target data and mishandled shared balances.
// A maintained integration must validate the chain and quote context,
// constrain supported destinations and isolate per-call accounting.
// See https://docs.0x.org/docs/upgrading/upgrading-to-swap-v2

2. Converting the ETH to DAI

Now we get to the heart of the logic. On a high level we do

  1. wrap ETH into WETH
  2. approve WETH for target
  3. execute 0x API swap
  4. run refunds

How you would get these parameters for the conversion, you can see in step 3. But essentially what they represent is an optimized swap contract call. swapTarget.call(swapCallData) executes the trade which internally transfers the WETH funds from this contract to the spender address.

Once the swap is finished, we can return any leftover ETH and DAI to the trader. The DAI refund might not be required as the leftover amount in my tests was always almost non existent. But keep it when doing large scale trades to be safe.

solidity
// Historical custom swap wrapper withdrawn in the 2026 review.
// It accepted arbitrary spender/target data and mishandled shared balances.
// A maintained integration must validate the chain and quote context,
// constrain supported destinations and isolate per-call accounting.
// See https://docs.0x.org/docs/upgrading/upgrading-to-swap-v2

3. Retrieving the API Request Data

The old Kovan /swap/v1/quote URL is retired. Current v2 requests use the documented endpoint, chain ID, taker and authentication/version headers. The execution fields now sit under transaction. Follow the v2 upgrade guide; the old three-field recipe is not a compatible request flow.

solidity
// Historical Kovan v1 URL builder removed.
// Build authenticated requests using the current API guide.
// API credentials belong in an appropriate off-chain service, not Solidity.

The allowance destination and execution destination have distinct roles. Follow the current API’s supported contracts and never grant a token allowance to Settler. A wrapper must not trust arbitrary destinations merely because a caller labels them as API output.

The withdrawn wrapper cannot be made safe by copying a new response into the old call. Rebuild and test the integration against its exact supported contracts and quote semantics.

4. Useful Helper Functions

Helper Ralph

If you wondered about some of the functions in the code above, don't worry. We've implemented and used a few useful helper functions, namingly concat, toStringBytes and getRevertMsg. Those may be quite useful to you in general, so it's worth taking a closer look.

1. Concat String Bytes

With the new abi.encodePacked function since Solidity v5, concatenating strings is particularly easy. You can use this function for strings like this: concat(bytes(myString1), bytes(myString2)).

solidity
function concat(
    bytes memory a,
    bytes memory b
) internal pure returns (bytes memory) {
    return abi.encodePacked(a, b);
}

2. Uint256 to String Bytes

Inspired by the Provable code here, this function computes the string representation of a uint256 number returned as bytes array.

Strings in Solidity are UTF-8 encoded. The value 48 implies the character '0'. So to convert a number to the correct string, we essentially compute and store 48 + remainder of modulo 10 for each digit.

solidity
function toStringBytes(
     uint256 v
) internal pure returns (bytes memory) {
    if (v == 0) { return "0"; }

    uint256 j = v;
    uint256 len;

    while (j != 0) {
        len++;
        j /= 10;
    }

    bytes memory bstr = new bytes(len);
    uint256 k = len;
    
    while (v != 0) {
        bstr[--k] = bytes1(uint8(48 + v % 10));
        v /= 10;
    }
    
    return bstr;
}

3. Get Revert Message for Low-level Call

Lastly we added a function to retrieve the revert message from low-level contract calls. This allows us to give more detailed information about the revert reason. Implementation was taken from Stackexchange, a website you should check out whenever you can.

This legacy helper assumes ABI-encoded Error(string). Length alone does not establish that format. Custom errors, panic data, malformed data and empty failures need separate handling; do not feed arbitrary returndata into this decoder as a general error parser.

solidity
function getRevertMsg(
    bytes memory _returnData
) internal pure returns (string memory) {
    if (_returnData.length < 68)
        return 'Transaction reverted silently';

    assembly {
        _returnData := add(_returnData, 0x04)
    }

    return abi.decode(_returnData, (string));
}

Historical integration and limitations

The linked repository records the original v1 implementation. Kovan and the old API recipe are no longer a working test environment. For a new project, use the current 0x integration documentation and verify the complete design in an isolated test setup.