I'm created a Smart Contract with the abilities to execute a multi call to the 0x DeFi aggregator. I already have the logic on my centralised server who checks for an arbitrage opportunity. When an opportunity is found, the server calls my smart contract (let's called it ArbitraEdge from now on).
This is the contract of ArbitraEdge: 0x791F46b25376d0477d44bB80F05a378Dd7c8454f
It's verified so you'll see the whole code. ArbitraEdge's contract is pretty simple. It just manages the allowance of some tokens and it execute a multi call to 0x's contracts.
I've been working on this for a really long time but every time I call the "multicallArbitrage()" function inside ArbitraEdge, it usually returns this error:
"BEP20: transfer amount exceeds balance"
I thought that this error can be originated by two things:
At first I thought at a Slippage Error. After debugging a bit with Tenderly I remembered that 0x features a slippage protection and a price impact protection. That said, it means that my server already checks if it's a good opportunity and if it has a high slippage or a high price impact. If that's the case, it doesn't call the multicallArbitrage() function on ArbitraEdge's contract. Also I set the slippage to 30% just to test if it will get through anyway but unfortunately no. Looked at this, we can say that if the opportunity it’s not good at 100% from the params I set, the call should not reach the smart contract. So we can throw away (I guess) the possibility that the problem is originated by Slippage.
Allowance. I tried to take a look at it, but it seems all fine too. I always approve ALL the tokens both through ArbitraEdge and the 0x Smart contract. Let me explain better:
Usually an arbitrage opportunity on my server looks like this:
So as you can guess, it's an arbitrage wrapped in BUSD. In this way the consumer of ArbitraEdge only needs to approve BUSD on his hand. ArbitraEdge will approve the followings: BUSD, HFT, BUNNY. In ArbitraEdge you'll see that I have two functions: approveArbitrageBot() and approveMaxSpender(). Both of these sets the allowance for the contract itself and to approve 0x's contract.
0x provides a cool feature that on the api request it gives me a property called “allowance target” so i pass directly that as an allowance.
Looking at tenderly I see that the transaction fails after the second/third swap.
Some examples of failed transactions:
You can see the input of this transactions from the Tenderly debug transaction.