Portal’s Android SDK provides token swapping functionality through the portal.trading.zeroX API. This integration allows you to retrieve swap quotes, inspect available liquidity sources, and execute token swaps using 0x.
Overview
Using the 0x integration, you can:
- Fetch indicative prices for token swaps
- Fetch swap quotes between supported tokens
- Retrieve available liquidity sources
- Execute swaps by signing and submitting transactions
All swap execution is performed by submitting the transaction data returned by 0x using portal.request.
Prerequisites
Before using the 0x API, make sure you have:
- A properly initialized Portal client
- An active wallet with sufficient balance on the source network (see Create a wallet)
- 0x integration enabled in your Portal Dashboard (see 0x Integration) OR have a 0x API Key available
Using a Custom 0x API Key (Optional)
By default, Portal uses the 0x API Key that can be added through the Portal Dashboard to communicate with the 0x integration.
If you have a 0x API key that you want to test with locally, you can optionally include it in the request via the zeroXApiKey parameter.
lifecycleScope.launch {
val sourcesResult = portal.trading.zeroX.getSources(
chainId = "eip155:1",
zeroXApiKey = "YOUR_0X_API_KEY"
)
sourcesResult.fold(
onSuccess = { response ->
val sources = response.data?.rawResponse?.sources ?: emptyList()
println("Available sources: $sources")
},
onFailure = { error ->
println("Error: $error")
}
)
}
If you’ve configured your 0x API key in the Portal Dashboard, you can omit passing the zeroXApiKey parameter:
lifecycleScope.launch {
val sourcesResult = portal.trading.zeroX.getSources(
chainId = "eip155:1"
)
sourcesResult.fold(
onSuccess = { response ->
val sources = response.data?.rawResponse?.sources ?: emptyList()
println("Available sources: $sources")
},
onFailure = { error ->
println("Error: $error")
}
)
}
Getting a Price (Indicative)
Use portal.trading.zeroX.getPrice to retrieve an indicative price for a token swap without generating executable transaction data.
This method is useful for displaying prices, estimating swap outcomes, or building preview experiences without committing to a quote.
lifecycleScope.launch {
val priceRequest = ZeroXPriceRequest(
chainId = "eip155:1",
buyToken = "USDC",
sellToken = "ETH",
sellAmount = "100000000000000" // 0.0001 ETH
)
val priceResult = portal.trading.zeroX.getPrice(
request = priceRequest
)
priceResult.fold(
onSuccess = { response ->
val data = response.data?.rawResponse
println("Price: ${data?.price ?: "N/A"}")
println("Buy Amount: ${data?.buyAmount}")
println("Liquidity Available: ${data?.liquidityAvailable ?: false}")
},
onFailure = { error ->
println("Error: $error")
}
)
}
Getting a Swap Quote
Use portal.trading.zeroX.getQuote to fetch a swap quote from 0x with executable transaction data.
lifecycleScope.launch {
val quoteRequest = ZeroXQuoteRequest(
chainId = "eip155:1",
buyToken = "USDC",
sellToken = "ETH",
sellAmount = "100000000000000" // 0.0001 ETH
)
val quoteResult = portal.trading.zeroX.getQuote(
request = quoteRequest
)
quoteResult.fold(
onSuccess = { response ->
val quote = response.data?.rawResponse
println("Quote: ${quote?.price ?: "N/A"}")
println("Transaction: ${quote?.transaction}")
},
onFailure = { error ->
println("Error: $error")
}
)
}
Getting Liquidity Sources
You can query available liquidity sources supported by 0x using portal.trading.zeroX.getSources.
lifecycleScope.launch {
val sourcesResult = portal.trading.zeroX.getSources(
chainId = "eip155:1"
)
sourcesResult.fold(
onSuccess = { response ->
val sources = response.data?.rawResponse?.sources ?: emptyList()
println("Available sources: $sources")
},
onFailure = { error ->
println("Error: $error")
}
)
}
Checking Quote Issues
When getting a quote, you may encounter issues related to allowances, balances, or simulation. Always check for these before executing a swap:
lifecycleScope.launch {
val quoteResult = portal.trading.zeroX.getQuote(
request = quoteRequest
)
quoteResult.fold(
onSuccess = { response ->
val issues = response.data?.rawResponse?.issues
issues?.allowance?.let { allowance ->
println("Allowance issue: actual=${allowance.actual}, spender=${allowance.spender}")
}
issues?.balance?.let { balance ->
println("Balance issue: actual=${balance.actual}, expected=${balance.expected}")
}
issues?.simulationIncomplete?.let { incomplete ->
if (incomplete) {
println("Warning: Simulation incomplete")
}
}
issues?.invalidSourcesPassed?.let { invalidSources ->
if (invalidSources.isNotEmpty()) {
println("Invalid sources: $invalidSources")
}
}
},
onFailure = { error ->
println("Error: $error")
}
)
}
Executing the Swap
Once you receive a quote containing transaction data, execute the swap by sending the transaction through portal.request.
The transaction data returned by 0x may include gas parameters such as gas or gasPrice. These fields are optional — you can omit them and let Portal estimate gas automatically, or include them if you prefer to use 0x’s suggested values.
lifecycleScope.launch {
// Get the quote
val quoteRequest = ZeroXQuoteRequest(
chainId = "eip155:1",
buyToken = "USDC",
sellToken = "ETH",
sellAmount = "100000000000000" // 0.0001 ETH
)
val quoteResult = portal.trading.zeroX.getQuote(
request = quoteRequest
)
quoteResult.fold(
onSuccess = { response ->
val quoteData = response.data
if (quoteData == null) {
println("Error: No data in response")
return@fold
}
val transaction = quoteData.rawResponse?.transaction
if (transaction == null) {
println("Error: No transaction in quote response")
return@fold
}
// Convert ZeroXTransaction to EthTransactionParam
val ethTransaction = EthTransactionParam(
from = transaction.from,
to = transaction.to,
gas = transaction.gas,
gasPrice = transaction.gasPrice,
maxFeePerGas = null,
maxPriorityFeePerGas = null,
value = transaction.value,
data = transaction.data,
nonce = null
)
// Submit the transaction
val sendTransactionResponse = portal.request(
chainId = "eip155:1",
method = PortalRequestMethod.eth_sendTransaction,
params = listOf(ethTransaction)
)
val transactionHash = sendTransactionResponse.result as? String
if (transactionHash != null) {
println("Transaction submitted: $transactionHash")
} else {
println("Error: Invalid response type for request")
}
},
onFailure = { error ->
println("Error: $error")
}
)
}
Error Handling
Handle errors gracefully when interacting with the 0x API:
lifecycleScope.launch {
val sourcesResult = portal.trading.zeroX.getSources(
chainId = "eip155:1"
)
sourcesResult.fold(
onSuccess = { response ->
val sources = response.data?.rawResponse?.sources ?: emptyList()
println("Success: $sources")
},
onFailure = { error ->
println("Error: $error")
// Handle specific error types
when (error) {
is PortalHttpException -> {
println("HTTP Error: ${error.message}")
}
is PortalHttpUnauthorizedException -> {
println("Unauthorized: Check your API key")
}
else -> {
println("Unknown error: ${error.message}")
}
}
}
)
}
Complete Swap Flow Example
Here’s a complete example of executing a token swap:
lifecycleScope.launch {
val chainId = "eip155:1"
try {
// Step 1: Get sources
val sourcesResult = portal.trading.zeroX.getSources(
chainId = chainId
)
sourcesResult.fold(
onSuccess = { sourcesResponse ->
val sources = sourcesResponse.data?.rawResponse?.sources ?: emptyList()
println("Sources: $sources")
// Step 2: Get quote
val quoteRequest = ZeroXQuoteRequest(
chainId = chainId,
buyToken = "USDC",
sellToken = "ETH",
sellAmount = "100000000000000" // 0.0001 ETH
)
val quoteResult = portal.trading.zeroX.getQuote(
request = quoteRequest
)
quoteResult.fold(
onSuccess = { quoteResponse ->
val quoteData = quoteResponse.data
if (quoteData == null) {
println("Error: No data in response")
return@fold
}
val transaction = quoteData.rawResponse?.transaction
if (transaction == null) {
println("Error: No transaction in quote response")
return@fold
}
// Step 3: Submit transaction
val ethTransaction = EthTransactionParam(
from = transaction.from,
to = transaction.to,
gas = transaction.gas,
gasPrice = transaction.gasPrice,
maxFeePerGas = null,
maxPriorityFeePerGas = null,
value = transaction.value,
data = transaction.data,
nonce = null
)
val sendTransactionResponse = portal.request(
chainId = chainId,
method = PortalRequestMethod.eth_sendTransaction,
params = listOf(ethTransaction)
)
val transactionHash = sendTransactionResponse.result as? String
if (transactionHash != null) {
println("Transaction submitted: $transactionHash")
} else {
println("Error: Invalid response type for request")
}
},
onFailure = { error ->
println("Error getting quote: $error")
}
)
},
onFailure = { error ->
println("Error getting sources: $error")
}
)
} catch (error: Throwable) {
println("Error: $error")
}
}
Supported Networks
The portal.trading.zeroX API supports a predefined set of EIP-155 networks. Requests using unsupported chains will fail.
| Network | EIP-155 Chain ID |
|---|
| Ethereum | eip155:1 |
| Optimism | eip155:10 |
| BSC | eip155:56 |
| Unichain | eip155:130 |
| Polygon | eip155:137 |
| Worldchain | eip155:480 |
| Mantle | eip155:5000 |
| Base | eip155:8453 |
| Monad Testnet | eip155:10143 |
| Mode | eip155:34443 |
| Arbitrum | eip155:42161 |
| Avalanche | eip155:43114 |
| Ink | eip155:57073 |
| Linea | eip155:59144 |
| Berachain | eip155:80094 |
| Blast | eip155:81457 |
| Scroll | eip155:534352 |
Next Steps