Brief (Really) Breakdown on Reflection Token Smart Contracts
librehash
Posted on October 5, 2022
this was originally written and published back last November (2021); obviously, we've learned a decent amount about such smart contracts in the time since - so keep this in mind when reading
this was the write-up that got crushed when my computer froze ; 16GB of RAM on her, but with nearly 2-years under her belt, its time to put her out to pasture soon enough -- not today, though! So let's fucking get to it without wasting any more time
This was mentioned before in the Discord, but some individuals from Binance(?) under the guise of 'Huh Token' reached out to give me a few tasks.
I'm going to take some time briefly to summarize those tasks to:
Give readers a better sense of the expertise of the one proposing this project idea
A better understanding of the concept of 'reflection tokens', overall
An intuitive understanding of just how difficult it is to instantiate such a project (for those that may be wondering, 'If this is such a good idea, how come everyone is not doing it?')
A sense of the general smart contract auditing pipeline for those that are unfamiliar with it.
Starting with the Certik Audit
This encounter was actually the first time I had ever come face-to-face with a smart contract that had been audited by Certik.
As we all know, Certik has caught a ton of flack over the last year or so after several projects they reportedly audited ended up having their smart contracts exploited / plundered (for one reason or another). Reviewing their audit was an activity I found to be enlightening because it ended up shifting my opinion on Certik from them being an incompetent auditing firm to them being a firm that has received unnecessary flack.
If that statement is surprising, then you should continue reading into the next section.
Spotting Simple Errors in the Smart Contract
Before taking a look at the Certik audit, I decided to delve into the smart contract code directly myself.
Specifically, I wanted to take a look at the 'HuhFlattenToken.sol' contract they had under the git repo (forked / branched over to my account).
You can find that here: https://github.com/Librechain/HuhToken/blob/main/contracts/HuhTokenFlatten.sol
You'll notice the errors begin around line 532 (we'll start from the BEP20 interface)
function distributeDividend(address shareholder) private {
if (shares[shareholder].amount == 0)
return;
uint256 amount = getUnpaidEarnings(shareholder);
if (amount > 0) {
totalDistributed = totalDistributed.add(amount);
(bool success,) = payable(shareholder).call{value: amount, gas: 30000}("");
require(success, "distributeDividend: Could not transfer funds!");
shareholderClaims[shareholder] = block.timestamp;
shares[shareholder].totalRealised = shares[shareholder].totalRealised.add(amount);
shares[shareholder].totalExcluded = getCumulativeDividends(shares[shareholder].amount);
}
}
Let's check out some of the comments that were made in that Certik Audit.
As we can see, the same code was identified & Certik is indeed correct here in stating that there is a major flaw here in the smart contract code that could lead to the draining of the smart contract.
Toward the beginning of the audit, a summary of the details uncovered can be found. Within that summary, we can see that the team had done little to nothing to rectify all of the issues published by Certik at the time (likely this is still the case since they do not have the expertise on hand to rectify all of the problem issues they identified).
From the image above, we can see that the vast majority of issues (of all severities) that were identified by the Certik team were not addressed (even though the project owners told me they were planning on launching at any moment).
This is indicative of how lazy + sloppy many smart contracts are 'in the wild' of blockchain (esp. as it pertains to BSC projects).
To be clear, the contract that I was looking at had:
Various 'reentrancy' flaws
No sanitized inputs for the contract
No implementation of 'SafeMath' (safemath.sol) to prevent buffer overflows on the reads (which would lead to memory corruption)
Main issue (from the Certik audit) was their failure to make sure that they 'zero out' the intermediate address balance of whomever has been granted permission to make a call on the contract (doing this is also necessary to make sure that the smart contract cannot be drained).
Addressing the Remaining Tasks Assigned for the Smart Contract
They threw me this very complex smart contract where they were looking to provide a "referral" for buyers of the token in the form of $BNB.
Referrers get 10% of the purchase amount from the contract. They also need to have their addresses whitelisted (+ referrer code; they insisted). Additionally, there is a tax applied to sellers to incentivize folks not selling any of this .
Normal seller tax = 15%; but if someone is a whistelisted seller, then the tax reduces itself down to 10% (in $BNB).
That tax then gets redistributed to those that are "providing liquidity". Some portion goes to those that are whitelisted referrers, then another portion goes to those that are "holders" of the
token.
And this all needed to be mounted up on a DEX (PancakeSwap specifically).
Specific Requests / Tasks to be Completed
Set things up so that this all would occur automatically (assuming someone possesses the requisite 'referral code' pointing back to th e referee).
I guess as an added twist, they failed to give me the actual smart contract code for the token / contract they were looking to launch.
They also didn't tell me who was responsible for creating the code that they already had in place at the time they asked me if I could do this. So there was nobody I was able to speak to about the situation to "catch up" and see where they were at with things.
So had to find the contract & then start out from scratch.
This wasn't all though.
Once I did find the actual code for the smart contracts (on GitHub), I had to walk through the smart contracts to get a sense for what it was doing / not doing (never seen one like this before).
There were some new functions in the code that I had never seen before (this is for Binance Smart Chain; not Ethereum).
Essentially they said their problem is that when a user made a call on the contract, there would be no automated transactions (this is impossible w blockchain anyway); but then they said whenever they made a subsequent transaction (initiated as the contract owner), that's when they would see the referral fees get paid out (but only during this event).
Identifying the Contract's Critical Function
When they said that, I knew I needed to look specifically at the part of the code that defined a regular, garden-variety transfer.
That's when I noticed the functions I had never seen before.
_rOwned
& _tOwned
; never seen that construction in my fucking life in a smart contract. So that's when I went a-hunting.
Turns out that this concept derives from some project launched this year called 'reflect finance' ; https://reflect.finance/
Hardly any information on their website detailing what these functions are supposed to be doing in the code. Its important for me to figure this out because these "r"-prefixed balances were all over the place in the code.
Other Developers Stumped on these 'rToken' Values as Well
When I attempted to search online to glean more information about these variables in the code, I was surprised to find that there were others in the same boat as I seeking the same information.
One potent example can be found here - https://forum.openzeppelin.com/t/reflect-finance-variables/6888
The first response that this user received, however (from a 'Top Contributor'), was a bit disheartening.
Worst yet, it appeared that not even the modertors for the token had a firm understanding of what these variables mean/meant.
Reading through more replies, it became immediately apparent that there were few that truly understood the code at all (since there are no 'code notes' & the documentation for 'RFI' is non-existent at the time of writing, which is highly unusual).
Finding the 'Holy Grail'
Fortunately for us, there is one user out there that decided to take the intiative of dissecting the functions in their entirety via a whitepaper.
That post on OpenZeppelin can be found here - https://forum.openzeppelin.com/t/a-technical-whitepaper-for-reflect-contracts/14297
Of course, this resource has long since been saved since it will serve infinitely useful in the mid-to-long-term as we parse out more information from here for inclusion into the whitepaper itself.
The URL for this whitepaper can be found here - https://reflect-contract-doc.netlify.app/
Defining Reflect Tokens
The paper starts off by providing the following helpful introduction to these token balance types: "Before diving into the contract, a new concept must be introduced: t-space and r-space values, along with tTotal and rTotal. tTotal, which belongs to t-space, represents tokens in circulation or total supply of a token. On the other hand, rTotal, which belongs to r-space, is a reflected value of tTotal. The term “reflected” cannot be easily explained in words but one interpretation of rTotal is token supply in reserve. Furthermore, values in t-space can easily be converted to r-space form, and vice versa using formula"
As well as: "Stakers are users who earn passive income by holding native token. In contrast, non-stakers do not earn rewards. Router contracts, pair contracts, dev wallets are usually excluding from staking in order to fully reward users."
This results in the following graph (showing the 'workflow' for these types of tokens).
Which is powered by the following deflationary mechanism:
Posted on October 5, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.