SafeMoon clones with renounced ownership might be lying to you

Cailen Fisher
4 min readMay 12, 2021

--

EDIT: 5/31/21

This has been updated/corrected to reflect the fact that it is actually possible to tell if this method has been used, by checking the _lockTimer variable. Thanks to Christoffer for calling this out!

SafeMoon clones have the ability to fake renounced ownership and it might not be obvious for an investor to tell if that has happened.

Renouncing ownership is achieved by setting the owner address to 0 (the dead address). The value of doing this is debatable, but people like it because it means the creator can’t manually drain liquidity or change the contract. It is considered a feature of a safe, non-scam project and is often a primary selling point.

I am honestly shocked that I haven’t seen any discussion of this, especially with so many of these tokens passing first class audits but here’s the deal:

SafeMoon’s contract code has a very clear potential backdoor in it for tokens advertising renounced ownership.

The code that achieves renouncing ownership looks like this:

function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}

That example is directly from the SafeMoon code. Nothing wrong with it, it does exactly what it is supposed to do. Owner is now set to 0 and the creator can no longer make changes or call any functions that are OwnerOnly.

The problem is, there are also two other functions in that section of the contract code: lock and unlock

//Locks the contract for owner for the amount of time provided
function lock(uint256 time) public virtual onlyOwner {
_previousOwner = _owner;
_owner = address(0);
_lockTime = now + time;
emit OwnershipTransferred(_owner, address(0));
}

//Unlocks the contract for owner when _lockTime is exceeds
function unlock() public virtual {
require(_previousOwner == msg.sender, “You don’t have permission to unlock”);
require(now > _lockTime , “Contract is locked until 7 days”);
emit OwnershipTransferred(_owner, _previousOwner);
_owner = _previousOwner;
}In theory, these could be used for a legitimate reason; locking ownership for a specified period of time before restoring it to make changes with community knowledge. But here’s the problem, I have not personally seen that use case even once but what I have seen is countless SafeMoon clones (and forks) that have this code and are advertising “Renounced Ownership” as a selling point and demonstrating that the owner address is indeed set to 0.

This might be entirely innocent in some cases, and if the creator (“dev”) didn’t call the lock function, then the _previousOwner variable would be empty and the flaw is harmless.

The real problem is that it may not be immediately obvious to an investor who is just checking the owner variable.

this will look the same even if _previousOwner is set

While the _lockTimer variable is publicly viewable, the _previousOwner variable is not.

unlock time is readable, but will be 0 by default

This means that when looking at a token that advertises renounced ownership based on SafeMoon code, you need to check the _lockTimer via the public geUnlockTime function. If the value is anything other than 0, this means that they can reclaim ownership at any moment after launch to drain the liquidity pool or make any other changes that might result in a rugpull or honeypot situation and to the casual observer the token contract would look the same as one where ownership was actually renounced!

From a pure software development standpoint, I can understand companies like Techrate passing this code in audit since in theory it could have valid uses, but in the context of actual applications, with many of these tokens advertising Renounced Ownership as a major feature, this is honestly a pretty big failing in open reporting and scam prevention. It is also a pretty big flaw in the SafeMoon contract code in the context of it being used as one of the most popular contract templates. A simple fix would be to (at the very least) make _previousOwner publicly readable but quite frankly it should just be removed by default and restored in the rare use-case that it actually applies to.

Thanks for reading, and please send me feedback!

I’m also happy to answer any questions if you want to ping me on Telegram: @cailenfisher

--

--

Cailen Fisher

Web applications developer for as long as that has been a thing, now diving into the muddy waters of fringe cryptocurrency and loving it.