Rug pull analysis on Unichain
I was checking the token activity on Unichain today when I was surprised by these 2 tokens:
Source: GeckoTerminal
At first glance, these tokens share some suspiciously similar characteristics:
- ๐ Number of transactions
- ๐ฐ Huge volume
- ๐ Down 100%
- ๐ Same 1day age
Looks interesting, let's dive in.
Here are the two tokens contracts:
- Safemoon:
0xB6b8a178bA3FD5A6D459644A0e0fdEDD50c22062
- View in VSCode - LIBRA:
0x4f7EaDB7497EcEcF96E54B34128fdba5366fAb6c
- View in VSCode
Interestingly, the contract source code for both tokens is verified. You might think:
"If it's a scam, why would the deployer bother verifying the contract?" ๐ค
๐ โโ๏ธ Not too fast, let's see what's inside.
As a practice exercice, try inspecting the contract source code yourself. Look for suspicious functions or mechanisms.
Fake supplyโ
When we look at the chart, we see that a sudden sell of a big supply just crashed the price. It turns out that the owner of the contract managed to get 1,000,000,000,000,000
unit (one quadrillion) of each token and then sold all of them, causing the price to crash while at the same time, wipping out the liquidity.
If you check quickly this contract code, it appears legitimate:
uint8 public buyTax = 0;
uint8 public sellTax = 0;
uint8 private constant _decimals = 18;
uint256 private constant _tTotal = 10000000000 * 10 ** _decimals;
string private constant _name = unicode"LIBRA";
string private constant _symbol = unicode"LIBRA";
uint256 private maxTxAmount = _tTotal * 30 / 100;
uint256 private maxWalletAmount = _tTotal * 30 / 100;
uint256 private swapTokensAtAmount = _tTotal * 25 / 10000;
It looks like there is no buy/sell tax, a fixed supply, even some anti-bot protection mechanisms? Wow amazing.
But my suspicions started at this line:
uint256 private antiBotToken = 1177515749873114667354971703959658596215712956789;
antiBotToken
, this is an interesting name, also, why would a token address be encoded as a uint256
?
If you look further, you will see this function:
function logger() private returns (address) {
return address(uint160(antiBotToken));
}
Ok, so this number is getting converted back to an address. By using chisel
you can quickly evaluate this:
โฏ chisel
Welcome to Chisel! Type `!help` to show available commands.
โ uint256 antiBotToken = 1177515749873114667354971703959658596215712956789;
โ address(uint160(antiBotToken))
Type: address
โ Data: 0xcE41a1E3f34634970495c6416522428673F8C175
So we have a (contract) address: 0xcE41a1E3f34634970495c6416522428673F8C175
. Obviously, this contract is not verified.
Interesting. So we have an "antiBotToken" which is being used as a "logger" function, weird weird.
We can see that this contract is called whenever a log()
event needs to be done. But this is all fake. There is also a fake implementation of the save
function in the ERC20Extension
contract. It is to make you think that things are fine and harmless. But the real contract is probably more harmful. If this call reverts, it can block any transfer transactions for these tokens.
Where is this quadrillion tokens coming from?
Let's look back at the constructor:
constructor() {
_balances[owner()] = _tTotal;
feeWallet = payable(owner());
_balances[logger()] = uint256(int256(-1));
emit Transfer(address(0), owner(), _tTotal);
}
A balance of -1
? ๐ค
Hmmm, look closely: _balances[logger()] = uint256(int256(-1));
. We can't store a negative number as an unsigned integer (uint256
). This causes the value to underflow and change to a very big number.
You can check with chisel
:
โ uint256(int256(-1))
Type: uint256
โ Hex: 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
โ Hex (full word): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
โ Decimal: 115792089237316195423570985008687907853269984665640564039457584007913129639935
Yup, that's a pretty big number. On top of it, this amount does not appear in the totalSupply
function.
LIBRA balance of the
antiBotToken
contract on Uniscan
Total LIBRA supply of the token on Uniscan
Lovely, so let's mint tokens with an apparent limited supply of 10B but let's also mint an unlimited amount of tokens to our weird antiBotToken
contract. Later we can just send these tokens back to the contract owner who can sell all of them at once.
If we look at the token transfers of this antiBotToken
contract, we can see this same pattern again and again:
Source: Uniscan
Trading volumeโ
On top of faking the supply, it seems quite obvious that the volume is fake.
Source: GeckoTerminal
There is an interesting trend, it looks almost too perfect to be "organic". I didn't look into the details of this but I'm quite sure there are some bots involved to pump the volume.
Conclusionโ
The scammer managed to get +200ETH (113ETH and 92ETH) in a bit less than 2 days for these 2 tokens alone. This money is being spread across many accounts which might be used to continue the scam to create fake volume on the following tokens.
- Create a "good looking" token contract with verified source code*
- Mint yourself an unlimited amount of tokens and fake the
totalSupply
function - Pump the volume and price of the token
- Sell 1 quadrillion tokens at once
- Profit
- Repeat
*Not verified is more suspicious than "good looking" verified contracts, right?
โ Be careful when trading coins. A verified source code does not mean it's safe.
There is nothing really new here, it's just a reminder to be careful when trading random coins - even if they are verified. Such scams can be done on any chains, it is not specific to Unichain.
Stay safe !