How I reverse engineered WazirX APIs to build my mini-portfolio manager!

Harsimar Singh
4 min readOct 6, 2021

In my defence, I wanted this feature badly 🤷

WazirX is one of the leading crypto exchanges in India. They witnessed a whopping 2,648 😰 percent sign-up growth in 2021 alone, with over 7.1 Million users now its the largest crypto exchange of India! [source].

But because they were adding so many users by the day, their platforms were never built to support this kind of scale and it started to fail on users during the most critical of times.

All these things set aside the product also had a lot of key features missing some very basic ones such as portfolio management. The app didn't tell you straight away how much money have you invested? what is the current value of your investments? how much have you gained/lost? which is something you would want to check daily when we are talking about such highly volatile markets. After checking different forums/Twitter and talking to a few of my friends I realized that it was not just me but a lot of users who wanted these features. So I did what any engineer would have done, I fired up chrome inspect to see what was happening!

Getting current crypto prices

One thing I knew would be straightforward to get was current crypto prices since the web exchange shows you crypto prices even when you are not signed in it. So I thought it should be a simple API call and yeah that turned out to be the case.

price_url = 'https://x.wazirx.com/wazirx-falcon/api/v2.0/crypto_rates'def getCurrentPrices():
session = requests.Session()
session.headers.update({'api-key': api_key})
response = session.get(price_url)
if (response.status_code == 200):
return response.json()

The api-key here had nothing to do with the user, it is basically some authentication key internal to the system, god knows why they have this when anyone can basically check this from the chrome network inspection 🤷‍♂

Getting order history

Now that I had the crypto prices, if there was any way I could get my past orders I was all set for my script, but things started to get complex from here. While it was easy to figure out the endpoint for the orders call but there were so many headers in the request which was very hard to make sense of.

Initially, I thought I’ll put debug points on javascript inspect in chrome and figure out the flow — this sounded so easy in my head but what a mess these javascript files were. Since it was production mode all the JS files were minified and using random variables and method names. Now imagine yourself going through a codebase where method and variable names were alphabets 🤦‍♂.

Headers in the rest call->
signature
: 2010f93c9b564dfcb4c0e80dd6d857bf9ab92b962a39c449b20731d7e7c10312
tonce :
1633537126909
access-key: <some_access_key>
api-key: <some_api_key>
<+ more>

The signature header was a hash, now the problem was because in inspect mode all the methods were named as alphabets (method awith arguments b,c calling method d) It was very difficult to figure out what hashing algorithm it is? what is the message? what is the secret? After a lot of time and debug points, I was able to figure out how it was getting generated:

When you sign in you are given two keys access_key and secret_key, (interestingly I am using the same access and secret keys for over 3 months now, which probably means they don't rotate the key — which is a big security threat — if these keys are compromised, it gives the hacker complete control of your portfolio) the latter (secret_key)is the key of the hash and the msg is [Note: tonce is the timestamp in millis of the request]

{httpMethod|access-key={accessKey}&tonce={tonce}|{requestUrl}

The algorithm was HMAC SHA 256, here is how I automated it in my script

def getTonce():
return int(time.time()*1000)
def hmacSha256(key, msg):
signature = hmac.new(bytes(key , 'latin-1'), msg = bytes(msg , 'latin-1'), digestmod = hashlib.sha256).hexdigest().lower()
return str(signature)
def formatSignature(httpMethod, accessKey, tonce, requestUrl):
return "{0}|access-key={1}&tonce={2}|{3}".format(httpMethod, accessKey, tonce, requestUrl)
def getOrders():
session = requests.Session()
tonce = getTonce()
signature = getSignature("GET",access_key,tonce,'/api/v3/orders|limit=50&order_by=desc&states[]=cancel&states[]=done',secret_key)
session.headers.update({'access-key': access_key, 'api-key' : api_key,'tonce' : str(tonce),'signature' : signature})
response = session.get(orders_url)

Post this it was easy-peasy, I had my past orders along with all the other stats and I had current crypto prices, I just had to do some computations on the data and show the output in a readable manner. Here is how it looked in the end

Python script output

Conclusion

There were no malicious intentions behind this project, I was just a desperate user who wanted a basic feature, and luckily I had the right skill set to understand the flows and build it on my own. Initially, I wanted to go with a chrome extension with proper UI but due to limited time, I went with a python script instead. If someone is interested in developing some tool here is the link to the code for reference.

--

--