Arbitrage party!! between PancakeSwap and BakerySwap (ApeSwap)

Yuya Sugano
14 min readOct 3, 2021

--

New updates with a few takeaways on April 1st in 2022.

This is the rewritten article of the Japnaese version that I posted in the past and the content might be obsolete now because of other chain’s adoptions such as Polygon, Cardano, Polkadot and Solana. In the first place, BSC (Binance Smart Chain) was captivating for me to look for newer arbitrage opportunities with the violent increase of gas price in Ethereum around this April. A lot of fork projects (DEXes and Lending protocols) emerged at that time in BSC, no doubt PancakeSwap and BakerySwap did so. So I swung over to BSC for reasonable fee prices to cover incurred costs of smart contract deployment and transactions. I was striving to make arbitrage profitable but there were little chance for me. However there would be room to improve the code with a conjuction of some ideas that I couldn’t have. So let’s jump in arbitrage party for pancake & bakery ($_$)

Disclaimer

This article is not either an investment advice or a recommendation or solicitation to buy or sell any investment and should not be used in the evaluation of the merits of making any investment decision. It should not be relied upon for accounting, legal or tax advice or investment recommendations. The contents reflected herein are subject to change without being updated.

Image by ArtCoreStudios from Pixabay

Takeaways April 1st 2022:

  • Fixed the version of truffle/hdwallet-provider above 2.x.x because of the error occurred for deploying a contract and migration. Downgrading the version to 1.2.2 also seems working. https://github.com/trufflesuite/truffle/issues/3936
  • Merged and referred to this repo’s code because the code looked more sophisticated and generalized. The repo referred to my github repo but she/he has improved the idea and code more than mine.
  • Added some testnet codes to confirm Flasswap feature in Uniswap forked projects.
  • Consider DEXes
  • What is FlashSwap?
  • Let’s test a swap
  • Monitor arbitrage opportunity
  • FlashSwap smart contract

I published an open repository in github at that time for informational and educational purpose and I didn’t know why but a lot of people forked the repo and starred so I felt a little responsible for writing something down to explain about that repo for people who’d look for more information about it. Also I wanted to evaluate the code and tweak it for more efficient and effective acting in real cases. [1]

I don’t cover much about differences between EVM-chains (Ethereum and BSC), what DEX and arbitrage are and about smart contracts, solidity language, I can’t count them all but I assume that readers have some ideas and knowledge about those blockchain things in advance. OK. First I’ll delve into why I chose PancakeSwap and BakerySwap to be looking for arbitrage opportunity in the following paragraph.

Consider DEXes

Where do we begin? Let’s think of what strategy we have to use. As explained well in Investopia there are simple and triangular arbitrage strategies basically when you choose an arbitrage strategy. Simple arbitrage, as derived from its name, is a simple strategy to exploit variations of the price of the same or similar assets in different markets or in different forms while triangular arbitrage consists of multiple exchange tradings with discrepancies of asset rates in those target exchanges. Let me illustrate quickly:

Simple Arbitrage:
1. Buy 1 BTC at the price 50,000 USD in the exchange A
2. Sell 1 BTC at the price 50,100 USD in the exchange B

As a result, you will earn 100 USD profit from this trade. Why can this happen? Arbitrage can exist as a result of market inefficiencies and it both exploits those inefficiencies and resolves them. This is nature in the market.

Triangular Arbitrage:
1. Buy 1 BTC at the price 50,000 USD in the exchange A
2. Trade 15 Eth at the rate 1 BTC in the exchange B
3. Sell 15 Eth at the price 50,100 USD in the exchange A

As a result, you will earn 100 USD profit from this trade. This strategy differentiate trading pairs from simple strategy. It might consist of multiple middle pairs and exchanges to exploit discrepancies of rates. For example, we can trade 15 Eth with 100 BNB before going back to USD in the exchange A.

There are some takeaways for considering arbitrage generally as mentioned in the following points. These characteristics exist in the traditional exchanges for arbitrage activities, rather affect DEX & protocol considerations in DeFi.

  1. Price differences are marginal so that making your trade profitable needs an immense amount of capital (-> affect DEX consideration)
  2. Inefficiencies in the rates are usually acted upon quickly, and the opportunity is eliminated, often in a matter of seconds (-> affect Protocol consideration)

In my understanding deviation of the rates should be more stable in larger volume exchange because a lot of people and bots trade assets and the price gets convergent quickly to the fair value. On the other hand under smaller volume exchange (TVL) there are not enough people and trades to make it happen fast. If these assumptions are true, we need to choose at least two exchanges the one is high TVL and vibrant exchange (in this case PancakeSwap) and the one is more smaller exchange with less TVL & less trades (in this case BakerySwap, ApeSwap). This situation possibly finds some fluctuations between the rates of trading assets through the exchange A to the exchange B.

But why? Let’s say you bought 1 BTC with 50,000 USD in the exchange A. When this transaction is made, we can assume that the rates in the exchange B have not changed yet in a short time of period. So the given 1 BTC could be traded at more higher value than 50,000 USD for a particular asset in the exchange B. Please note that rational actors and bots might have set arbitrage mechanism already to ensure that prices do not deviate substantially from fair value for long periods of time across the exchanges. So it gets harder and harder to profit from this strategy as the market and exchanges mature. This is the reason that I said it might be obsolete already in the preface.

Are you in a maze? It might sound too complicated. Sorry about that. Please do not hesitate to raise an issue or contact me on Twitter if something is unclear. We are now seeing the aforementioned characteristics and we need to resolve these problems 1) need an immense amount of capital, 2) need to execute transactions swiftly. Here’s the answer to cope with these two problems in DeFi world. It is called Flashloan.

Long story short, Flashloan let us to borrow an immense amount of liquidity from lending pools or AMM-based pools at a moment and we can get capable for doing our strategy in a single atomic transaction. If this transaction fails for some reasons then the transaction gets reverted as if nothing had happend. You can notice that two problems can be settled with this magical nature of Flashloan now. What we need to do is to choose nicer DEXes that support Flashloan feature in underlying protocol level.

Flash Loans are special uncollateralised loans that allow the borrowing of an asset, as long as the borrowed amount (and a fee) is returned before the end of the transaction. There is no real world analogy to Flash Loans.

Even if Flashloan can provide sufficient liquidity for performing arbitrage, there should be profitable amount of liquidity of assets to withdraw in target exchanges at least. It means that the condition №1 also affects DEXes selection. If you go with lending protocol such as Multi-Chain Lend(MCL) you have to check pool’s liquidity and if you go with AMM-based protocol PancakeSwap & BakerySwap then you have to check liquidity pool’s amount.

The reason I chose PancakeSwap & BakerySwap was that these are both Uniswap forked protocols so that I could use Flashloan in protocol level and I thought they had sufficient liquidity in liquidity pools for doing our arbitrage strategy. The github codes are intended to use Uniswap forked protocols only. If you wanted to use other DEXes please do your own research if that DEX supports Flashloan and has the same interfaces to run the codes.

If you go to BscScan & DappRadar you will find a lot of DeFi protocols running in BSC and you will need to confirm TVL in protocols, liquidity in pools and if those services support Flashloan feature in code base. Please make sure that the condition №1 can be met based on your research. I just traced back what I did in the github repo and used PancakeSwap and BakerySwap(ApeSwap) again. Choice is yours.

BscScan — Yield Farms
DappRadar — BSC DeFi

Now let’s talk about the next condition №2. According to this article by Julien, there are multiple flashloan providers (protocols) Aave, dYdX and Uniswap. These DeFi services have implemented unique Flashloan feature in each protocol, for the sake of the condition №2 all protocols actually can meet the requirement that performs borrow, use tokens and reimburse tokens in a single transaction. As long as I investigated there is no dYdX forked project in BSC yet. We can choose Aave and Uniswap type in this situation. [2]

You might think already that we need to leverage lending protocols such as Aave or dYdX to get some tokens before doing arbitrage in DEXes, but wait Uniswap DEXes have Flashloan feature that is called FlashSwap already. Since Uniswap V2, all swaps are FlashSwap under the hood as mentioned in the V2 document. In an atomic transaction you’ll be able to receive output tokens in a pool before you pay back input tokens. I know this sounds bizarre. Please go to the documentation and you’ll understand the concept clearly. [3]

Flash Swaps — Uniswap V2

Now I think you have determined your arbitrage strategy, usable DEXes with your choice and Flashloan protocol that will make your strategy fly.

What is FlashSwap?

In this paragraph we dive into FlashSwap. First FlashSwap can be deemed as a form of Flashloan by its nature so that we can borrow an immense amount of tokens before paying back to liquidity pools and use those tokens for our arbitrage strategy in a single transaction. If our strategy didn’t work in a block that transaction would be reverted as if nothing had happened. You will lose a bit of BNB (BSC’s native token equivalent to Eth in Ethereum) for transaction fees in this case.

The most important point in FlashSwap is that you have to set bytes call data to greater than 0 in swap function so that the pair contract can send tokens to to address because the contract assumes that payment has already been received. Then the pair contract will trigger the callback function on to address to receive payback in liquidity pool. Here’s the order of this FlashSwap in a nutshell. These consecutive events should happen in a single transaction therefore it will be able to happen in a block.

function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data);
  1. Your smart contract calls swap function in a pair contract with bytes call data greater than 0
  2. The pair contract assumes input tokens have been sent and sends out output tokens to your smart contract to address
  3. The pair contract triggers the call back function uniswapV2Call on our smart contract to address
  4. Your smart contract must reimburse required amount of input tokens to msg.sender who is the pair contract from uniswapV2Call

Please do not forget that Uniswap protocol takes 0.3% LP fee for trading plus be careful about different LP fees in other forked projects. PancakeSwap has set 0.2% fee. You have to reckon with this fee when you return tokens.

Callback function names are different in protocols. UniswapV2 has its name unipswapV2Call but PancakeSwap has set a callback function named pancakeCall . You have to do your own research in code base.

PancakePair.sol

Here’s a snippet of my sample contract. This function has set bytes call data with padding not empty string. It should work without any issues for doing FlashSwap.

Let’s test a swap

Do we pack in and enjoy a party now?? nope nope. Let’s do a deliberate plan and test it before going to a real party. I was using Truffle + node.js (JavaScript) + web3.js but Hardhat + TypeScript + ethers.js is getting more momentum and traction these days. The latter combination can give you more development agility and efficiency. It is up to you what tools to use. Here are my local versions of the used tools in the repo.

$ sudo n lts // install the lts node.js
$ node -v
v14.17.6
$ truffle version
Truffle v5.3.7 (core: 5.3.7)
Solidity - >=0.6.6 <0.8.0 (solc-js)
Node v14.17.6
Web3.js v1.3.6

I developed a simple smart contract to invoke swapExactTokensForTokens function from PancakeSwap RouterV2 and also a node.js script to call my smart contract that has been deployed in BSC mainnet. The reason I deploy a smart contract in mainnet is that there seems like no actual liquidity and a proper service in BSC testnet. So this test is done in the actual working mainnet and the actual liquidity pool from PancakeSwap. I set a test amount 0.005 BNB for testing purpose in .env.

Upgrading truffle/hdwallet-provider above 2.x.x or downgrading the version to 1.2.2 can fix the deployment problem. https://github.com/trufflesuite/truffle/issues/3936

I needed to deploy a Swapcontract smart contract in Remix because the endpoints did not work well configured under truffle-config.js and running truffle migrate --network mainnet failed with the official endpoints and even the third party Moralis.io endpoints. Here’s the error. If you face the same error please try to use Remix to compile and deploy your smart contract in BSC accordingly. Select “Injected Web3” and configure your wallet properly. You will see custom network number 56 in the environment.

only replay-protected (EIP-155) transactions allowed over RPC

Estimating gas cost and getting gas price did not work well in web3 library. I didn’t know if this can happen in web3 only or it matters because of BSC network. This code snippet works fine in normal Ethereum network but an error was cast in BSC. I did hardcode gasPrice and gasCost by checking gas analysis service like this. gasPrice should be fine with 5Gwei in BSC.

const [gasPrice, gasCost] = await Promise.all([
web3.eth.getGasPrice(),
tx.estimateGas({from: admin}),
]);

It’s time to test. This swap.js script finds a rate for USDC/WBNB and swap 0.005 WBNB with USDC. The script orders the deployed smart contract to talk to PancakeSwap Router and swap 0.005 WBNB from liquidity pool for USDC, once the smart contract receives output tokens (USDC) it sends the received tokens back to msg.sender who is your wallet account. Here’s my result.

$ npm run swap
> node swap.js
pairAddress USDC/WBNB is 0x30479874f9320A62BcE3bc0e315C920E1D73E278
Trading USDC/WBNB ...
Input amount of WBNB: 0.005
tokenIn: 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c, tokenOut: 0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d
Swaping token at PancakeSwap DEX
=================
tokenIn: 0.005 WBNB
tokenOut: 2.117651883042604402 USDC
Transaction hash: 0xbdfc0dba124ec5f6ec5ed50218ebxxxxxxxxxxxxxxxxxxxx
0.005 WBNB was swapped with 2.11 USDC

Upgrading truffle/hdwallet-provider above 2.x.x or downgrading the version to 1.2.2 can fix the deployment problem. https://github.com/trufflesuite/truffle/issues/3936

Some takeaways:

  • Hardcode gasCost & gasPrice in a node.js script for BSC
  • Deploy a smart contract in Remix not from Truffle for BSC
  • Need to approve your smart contract to transfer from your wallet address

We ensured that PancakeSwap implemented the same interfaces of UniswapV2 and token swapping worked appropriately with a little amount of input tokens. In the next paragraph I’ll explain how to monitor arbitrage opportunity realtime across BSC DEXes (PancakeSwap and BakerySwap).

swap.jsa sample script to swap WBNB with USDC in BSC
Swapcontract.sola sample smart contract to interact with PancakeSwap RouterV2

Monitor arbitrage opportunity

I hope we’re close to juicy profits from arbitrage party. But wait how can we know 1) when to send a swap transaction, 2) if a transaction succeeds and profitable? Here is a monitoring script takes into account. We assume that we’ve got a large amount of input tokens (these will be received from PancakeSwap pool with Flashswap) and we can estimate how much profit we can get from that fund in a script. This can be calculated: Initial received output tokens minus returning input tokens, minus estimated transaction costs (gasCost & gasPrice). A simple mathematics.

When those conditions are met we can cast a transaction to our smart contract. If something gets wrong and the transaction gets reverted, nothing happens in your balance except little transaction fees that are incurred for executed operations at the point the transaction reverted. I also use web3 package to interact with PancakeSwap and BakerySwap interfaces. To keep running this monitoring node.js script, I introduced forever package so that your script can run continuously in background. Please install forever in your environment. [4]

// install forever globally and how to run node.js script
$ npm install -g forever
$ forever --version
v3.0.4
$ forever <your file name>.js // forever index.js
$ forever list

I was cautious about WBNB/BUSD and WBNB/USDC pools in Bakery swap are depleted already. So you should choose other DEXes not BakerySwap for your strategy. It looks like there is no fair and proper liquidity in BakerySwap pools. The new contract code can cover multiple DEXes in BSC.

monitoring.js script is obsolete. Please find the new index.js .

I configured this script with WBNB/BUSD pair and WBNB/USDC pair for this arbitrage trade: 1) Swap 1000 WBNB with BUSD or USDC in PancakeSwap, 2) Swap output BUSD or USDC tokens with WBNB back in BakerySwap.

monitoring.js — a sample script that monitors a simple arbitrage opportunity across PancakeSwap & BakerySwap.

Looking for arbitrage opportunity in BakerySwap
ApeSwap make more sense

Flashswap smart contract

Finally we need a Flashswap smart contract to invoke Flahloan and execute profitable trades in a single transaction in PancakeSwap. As we have seen how Flashswap works in the previous paragraph, we need to call swap function with padding data in bytes call data for receiving output tokens prior to transferring input tokens to the pair contract. Then the pair contract calls the implemented callback function pancakeCall in your smart contract to execute arbitrage in BakerySwap(ApeSwap) and pay input tokens back to the pool. Off course, when an amount of payback tokens is not enough all consecutive orders can be cancelled and the transaction gets reverted.

Flashswap.sol is a sample smart contract to do all the things as below. I changed the target DEX from BakerySwap to ApeSwap and as there’s been little chance for arbitrage so I hadn’t seen this went successful with certain profits yet. I referred to this official example code for Flashswap part and this amazing contract code by Haenchen.

At last, ApeSwap seems to be thriving now. I didn’t confirm their contracts in depth but I think that ApeFactoryV2 and ApeRouterV2 are equivalent to UniswapV2 Factory & Router. That’s why I switched the target DEX from BakerySwap to ApeSwap. There are also other variants a lot. It’s worth to look through the document and try one!! Good luck.

ApeSwap for BSC

--

--

Yuya Sugano

Cloud Architect and Blockchain Enthusiast, techflare.blog, Vinyl DJ, Backpacker. ブロックチェーン・クラウド(AWS/Azure)関連の記事をパブリッシュ。バックパッカーとしてユーラシア大陸を陸路横断するなど旅が趣味。