Skip to main content

Rug pull analysis on Unichain

ยท 5 min read

I was checking the token activity on Unichain today when I was surprised by these 2 tokens:

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:

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.

tip

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.

Screenshot of Uniscan showing the bot balance LIBRA balance of the antiBotToken contract on Uniscan

Screenshot of Uniscan showing the total supply of the token 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:

Screenshot of Uniscan showing the minting of billions of tokens

Source: Uniscan

Trading volumeโ€‹

On top of faking the supply, it seems quite obvious that the volume is fake.

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.

  1. Create a "good looking" token contract with verified source code*
  2. Mint yourself an unlimited amount of tokens and fake the totalSupply function
  3. Pump the volume and price of the token
  4. Sell 1 quadrillion tokens at once
  5. Profit
  6. Repeat

*Not verified is more suspicious than "good looking" verified contracts, right?

warning

โœ‹ 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 !