Problems with Coinbase Withdrawal Fees
The other day I decided to have a little fun and see if I could get GPT to generate seed phrases that were coherent sentences. I ended up coming up with this one, which I funded with a little bitcoin and posted on X to see how long it would take to get swept. It lasted a whole 45 minutes!
What does this have to do with Coinbase? They played a small part in my experiment.
Wallet Funding Fun
For privacy reasons I didn't want to fund this from any of my bitcoin wallets, so I decided to just buy $100 on Coinbase and withdraw it to the giveaway wallet. It turned out I was in for several surprises!
Right off the bat, I didn't have $100 to send because Coinbase took a 3.5% cut of the purchase. Fair enough, they've gotta turn a profit for their service, right?
But then imagine my shock when I saw the withdrawal fee: $60! What's going on here?
I was understandably surprised because on-chain fees have been at rock bottom prices for the past month. This was the state of the recent and upcoming blocks at the time of my withdrawal request:
So what's the explanation? Well, there had been an abnormal spike in network activity about 3 hours prior due to some new service launching and creating demand for deposit transactions. It only lasted for a few blocks.
As we can see here, that spike in demand caused Bitcoin Core's fee estimates to shoot up from ~4 satoshis / vb to 500+ satoshis / vb.
I wrote at length about the many issues around estimating transaction fees back in 2017. It would appear that some of these issues are still lingering and Coinbase hasn't put engineering effort into improving upon Bitcoin Core's fee estimate algorithm.
Where it gets Sketchy
While there's clearly room for improvement on the engineering side, it seems there are some also some questionable business practices going on. Let's dig a bit deeper into my withdrawal transaction. I've underlined the mining fee in red.
Here's the thing: Coinbase, like many exchanges, is designed well enough that they batch their withdrawal transactions. This specific withdrawal transaction has 9 outputs, which saves a lot of block space and thus on-chain fees. With 9 outputs it's safe to assume that 1 was change back to Coinbase while the other 8 were customer initiated withdrawals. Naturally, this raises some questions for me.
0.00108976 BTC (fee I paid) ÷ 0.00279448 BTC (total fee) = 39%
Why am I paying 39% of a fee that is being split by 8 people?
What fees are the other 7 people paying to Coinbase?
If Coinbase charged all 8 of us 0.00108976 BTC but only paid a 0.00279448 BTC fee, that means they pocketed 0.0059236 BTC ($350 USD) from this one batch withdrawal.
Long story short, I think there's a strong case that I was egregiously overcharged for this withdrawal in not one but two different ways.
- My "fair share" of the batch transaction fee was less than a third of what I was charged (0.00034931 BTC rather than 0.00108976 BTC)
- The appropriate market rate for fees at the time was only 1% of what Coinbase was requiring. (7 satoshis per virtual byte rather than 778 satoshis per virtual byte)
It's particularly interesting to me that during this process Coinbase made $4 off of selling BTC to me but they may have pocketed $40 from my contribution to the withdrawal fee.
Recommended Improvements
- Stop simply using Bitcoin Core's fee estimation and develop something smarter that looks at the state of the mempool rather than only looking at data from the most recent day's worth of blocks, which can be slow to adjust in volatile conditions.
- Give users a choice of withdrawal fees / priority when requesting a withdrawal. It's important for the health of the block space market that fees are chosen by humans rather than purely by algorithms. Too much of the latter can create weird feedback loops.
- WARN the user when the withdrawal fee exceeds a nontrivial portion of the value sent, say 10% or so. This is pretty easy to miss when you're just looking at 8 digits of precision in a tiny number.
- Do a better job ensuring that users are charged for the on-chain fees that are actually paid. Perhaps that means refunding back overages when the withdrawal is processed in a large batch that enables the fee to be shared among many users. I will grant that there is a bit of a "chicken and egg" problem here because you don't know the final fee until a transaction is confirmed.
- Consider optimizing fees by initially underpaying based primarily upon the current state of the mempool, then automatically bumping your fees (and further optimizing the batch withdrawal by adding more outputs) as time passes if the transaction isn't getting confirmed.
It makes sense to me WHY Coinbase's withdrawal process is architected in this way. They're striving for the simplest and most convenient experience for their users, so they likely prefer to overcharge and get fast confirmations.
Closing Thoughts
While it might seem like this article is picking on Coinbase, I'd like to be clear that the fee estimation problems I outlined are not limited to them. For example:
I've also noticed similar questionable charging of disproportionately high withdrawal fees by other exchanges that benefit from batching transactions.
My point is that we can do better than these naive solutions that don't handle network volatility well. Onward!