Working in the Testnet
0x API and 0x Protocol can be accessed on the Goerli testnet, or by forking Ethereum mainnet into your own testnet.
A hosted 0x API for the Goerli testnet is available at https://goerli.api.0x.org/ which offers a subset of DEX sources available on Ethereum mainnet.
To view the currently supported sources on Goerli refer to https://goerli.api.0x.org/swap/v1/sources. At the time of writing this guide
0x
, MultiHop
, SushiSwap
, Uniswap
, Uniswap_V2
and Uniswap_V3
are supported. The token you want to use for testing must have liquidity on at least one of these sources.For general information about the /swap endpoints see 0x API Swap
The following is example code for swapping 0.1 ETH for DAI on Goerli using Metamask.
Instructions to use the CodePen:
- Click "TypeScript" to see the code. Click "Run Pen" to run the code demo.
Example code for swapping 0.1 ETH for DAI on Goerli using Metamask
Refer to @0x/contract-addresses to find the addresses of the latest deployment on Goerli and then follow our limit order related guides below, using the Goerli contract addresses, and chain id 5.
When forking Ethereum mainnet all current Ethereum state as of the block that you fork will also be available in your private fork, all contracts addresses, and balances tokens will be exactly like the Ethereum mainnet. The main advantage with this is that you can test quotes from our hosted Ethereum mainnet 0x API on your private forked network without having to run any additional infrastructure or spend any real funds on test transactions.
This guide assumes that you already have a working Hardhat project, if not please refer to the official Hardhat Getting Started Guide.
Configuring Hardhat to create a private fork
First, you need to add a
hardhat
network in your Hardhat config file.// hardhat.config.ts
// ...
const config = {
solidity: "0.8.4",
networks: {
hardhat: {
forking: {
url: RPC_URL, // This should be your Alchemy/Infura RPC URL
},
},
},
};
// ...
Now, for your tests, you can find an account on Ethereum mainnet with the appropriate balances required for the swaps that you want to test locally, then using the hardhat_impersonateAccount you can act as that account on your private fork. You can then request a valid quote for that account from Ethereum mainnet 0x API and submit the transaction on your private fork. Keep in mind that you may need to set allowances if you are trading with other tokens than Ethereum.
Example test swapping 1 ETH for DAI using an Ethereum mainnet 0x API quote
import { expect } from "chai";
import { ethers, network } from "hardhat";
// node-fetch version 2 needs to be added to your project
import fetch from "node-fetch";
const ONE_ETHER_BASE_UNITS = "1000000000000000000"; // 1 ETH
const MINIMAL_ERC20_ABI = [
"function balanceOf(address account) external view returns (uint256)",
];
describe("0x API integration", function () {
it("it should be able to use a 0x API mainnet quote", async function () {
// Quote parameters
const sellToken = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; // ETH
const buyToken = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI
const sellAmount = ONE_ETHER_BASE_UNITS;
const takerAddress = "0xab5801a7d398351b8be11c439e05c5b3259aec9b"; // An account with sufficient balance on mainnet
const quoteResponse = await fetch(
`https://api.0x.org/swap/v1/quote?buyToken=${buyToken}&sellAmount=${sellAmount}&sellToken=${sellToken}&takerAddress=${takerAddress}`
);
// Check for error from 0x API
if (quoteResponse.status !== 200) {
const body = await quoteResponse.text();
throw new Error(body);
}
const quote = await quoteResponse.json();
// Impersonate the taker account so that we can submit the quote transaction
await network.provider.request({
method: "hardhat_impersonateAccount",
params: [takerAddress]
});
// Get a signer for the account we are impersonating
const signer = await ethers.getSigner(takerAddress);
const dai = new ethers.Contract(buyToken, MINIMAL_ERC20_ABI, signer);
// Get pre-swap balances for comparison
const etherBalanceBefore = await signer.getBalance();
const daiBalalanceBefore = await dai.balanceOf(takerAddress);
// Send the transaction
const txResponse = await signer.sendTransaction({
from: quote.from,
to: quote.to,
data: quote.data,
value: ethers.BigNumber.from(quote.value || 0),
gasPrice: ethers.BigNumber.from(quote.gasPrice),
gasLimit: ethers.BigNumber.from(quote.gas),
});
// Wait for transaction to confirm
const txReceipt = await txResponse.wait();
// Verify that the transaction was successful
expect(txReceipt.status).to.equal(1, "successful swap transaction");
// Get post-swap balances
const etherBalanceAfter = await signer.getBalance();
const daiBalanceAfter = await dai.balanceOf(takerAddress);
console.log(
`ETH: ${etherBalanceBefore.toString()} -> ${etherBalanceAfter.toString()}`
);
console.log(
`DAI: ${daiBalalanceBefore.toString()} -> ${daiBalanceAfter.toString()}`
);
});
});
Last modified 1mo ago