Never deploy forked contracts

This is a bad story. The only good part of this story is that i was a spectator and not directly involved. (And always, the things we remember to avoid from happening again)

If there is something that really scares me, it's the possibility of people losing their money because of a mistake I made.

When i've started, i've always wanted to be 100% anonymous to cover myself from that kind of situation.
But that's not the solution, the solution is to be careful, write tests, think as an attacker and learn from the mistakes of others.

Can you fork a rebase contract?

Hell, i always hated those kind of contracts because i never liked an asset to morph while i was sleeping.
So i routed that guy to a dev that i know, asking if he could do the job.

Let's be honest, that was the usual random guy popping out of my Telegram, asking to launch a shitcoin.
If i would refuse the job, someone else would do it. (maybe it was better, in this case)
So i simply connected the two guys to do the contract.
He started forking this contract and doing some tests on bsc testnet.
All worked out after a couple of mins, checked the rebase event, balances, pools, everything nominal.

The launch

They launched the token, peoples were buying and sells were working as expected.
Suddently...

The sells were not working anymore... this happened sometimes during my journey but...
What did he said about LP?
Why the fee is higher? why buy & sells work now?
Wtf is going on???

NOT THAT AGAIN!
WTF!!!
I've instantly traced back the sell transaction, to discover it was the contract sell+addLiquidity & rebase kicking in... but where did those tokens came from?!

The bug

The owner address was set as liquidity receiver wallet.
The dev, after deployment, sent all the tokens & the ownership to a wallet...
but that wallet, was also the liquidity receiver...
And the liquidity receiver market-sold all his balance to add LP, but it had no limits!
The limits where on the addLiquidity call, but all the BNB was sent to the token contract itself.

At this point, well.. i knew the contract were fucked.
But that dev... he learned from me and my paranoia about those situations was already high.
So each time i had to deploy a shitcoin, i always added two functions, those that i call "better safe then sorry".

// better safe then sorry
function transferForeignToken(address _token, address _to, uint256 _value) external onlyOwner {
        if(_value == 0) {
            _value = IERC20(_token).balanceOf(address(this));
        }
        IERC20(_token).transfer(_to, _value);
    }
   
    function Sweep() external onlyOwner {
        uint256 balance = address(this).balance;
        payable(owner()).transfer(balance);
    }

Guess what?
No sweep function, funds are still locked inside that contract... forever. At the time of the episode, those were worth ~130k$.


I repaid around 15BNB from my personal wallet the original owners and recovered ~40BNB from the pool, then i vanished like i never did before, with the tail in my legs.
Knowing that, this should never happen to me. Ever.

What we learned

  • Never fork contracts or if you do, learn them first.
  • Write more complex tests even if this one was really tricky to catch
  • When a dev ask you to read a contract, read it fully like you're an auditor.
  • People can lose money because of you, even if you're not directly involved.
  • Even if a contract is working, it doesn't mean it will work with different states.
  • Always test 1:1 the contract & the configuration parameters before going on mainnet.
  • Do not use payable functions without a way to recover assets from the contract or make a sort of "auto-sweep" at the end of the function.