Implementing NFT Purchase in a Mobile App
Buying an NFT means calling a marketplace smart contract function, passing the required ETH or ERC-20 amount. Sounds simple, but between clicking "Buy" and a confirmed transaction are several steps where things go wrong: gas depleted, NFT already sold, wallet not connected. UX must handle each case.
Checking Listing Availability Before Purchase
Before showing the "Buy" button, verify the listing's current status. The NFT might have sold a second ago; the cached screen doesn't know.
// iOS — checking listing status before purchase
func checkListingActive(contractAddress: String, tokenId: BigUInt) async throws -> Bool {
let listing = try await marketplaceContract.getListing(
nftAddress: EthereumAddress(contractAddress)!,
tokenId: tokenId
)
return listing.price > 0 && listing.seller != EthereumAddress.zero
}
If inactive — change button to "Not for Sale," no purchase dialog.
Approve + Buy: One or Two Transactions
Buying with ETH is one transaction: buyItem(nftContract, tokenId, { value: price }).
Buying with ERC-20 (e.g., USDC) is two:
-
usdc.approve(marketplaceAddress, price)— authorize the marketplace to spend tokens -
marketplace.buyItem(nftContract, tokenId)— actual purchase
Show this as a single unified flow: "Step 1 of 2: Approve USDC Spending" → "Step 2 of 2: Confirm Purchase." Progress indicator, explanation at each step.
Optimization: use permit (EIP-2612) if the token supports it. This combines approve and buy into one transaction via off-chain signature.
Awaiting Confirmation
After sending the transaction, don't block the screen. Display:
- TransactionHash as an Explorer link (Etherscan, Polygonscan)
- "Awaiting Confirmation" indicator with option to leave
- Push when N confirmations are received (usually 1–3)
// Android — waiting for receipt with timeout
suspend fun waitForReceipt(txHash: String, timeoutMs: Long = 120_000): TransactionReceipt? {
val deadline = System.currentTimeMillis() + timeoutMs
while (System.currentTimeMillis() < deadline) {
val receipt = web3j.ethGetTransactionReceipt(txHash).send().transactionReceipt
if (receipt.isPresent) return receipt.get()
delay(3_000)
}
return null
}
If receipt.status == "0x0" — transaction reverted. Decode the reason via debug_traceTransaction or check known contract errors.
Typical Errors and Handling
| Error | Cause | UI Response |
|---|---|---|
execution reverted: Not listed |
NFT delisted | "NFT no longer for sale" |
execution reverted: Price mismatch |
Price changed | Show current price, offer refresh |
insufficient funds |
Not enough ETH for gas | "Top up wallet for gas fee" |
| Transaction timeout | Network congestion | Offer to increase gas price |
Timeline: 3–5 days for listing check, approve+buy flow, confirmation awaiting with push, error handling.







